#include "sdk.h"
#include "M_Code_StyledTextCtrl.h"
#include "M_Code_colourmanager.h"
#include <wx/fontutil.h>
#include <wx/splitter.h>
#include "M_Code_editorprintout.h"
#include "M_Code_debugger_interfaces.h"
#include "editor_hooks.h"
#include "encodingdetector.h"
#include "filefilters.h"
#include "projectfileoptionsdlg.h"

const wxString g_EditorModified = _T( "*" );

#define ERROR_STYLE      wxSCI_MARK_SMALLRECT
#define BOOKMARK_STYLE   wxSCI_MARK_ARROW
#define BREAKPOINT_STYLE wxSCI_MARK_CIRCLE
#define DEBUG_STYLE      wxSCI_MARK_ARROW
#define DEBUG_STYLE_HIGHLIGHT wxSCI_MARK_BACKGROUND
#define BREAKPOINT_OTHER_MARKER    1
#define BREAKPOINT_DISABLED_MARKER 2
#define BREAKPOINT_MARKER          3
#define BOOKMARK_MARKER            4
#define ERROR_MARKER               5
#define DEBUG_MARKER               6
#define DEBUG_MARKER_HIGHLIGHT     7
#define C_LINE_MARGIN      0
#define C_MARKER_MARGIN    1
#define C_CHANGEBAR_MARGIN 2
#define C_FOLDING_MARGIN   3

const int idEmptyMenu = wxNewId();
const int idEdit = wxNewId();
const int idUndo = wxNewId();
const int idRedo = wxNewId();
const int idClearHistory = wxNewId();
const int idCut = wxNewId();
const int idCopy = wxNewId();
const int idPaste = wxNewId();
const int idDelete = wxNewId();
const int idUpperCase = wxNewId();
const int idLowerCase = wxNewId();
const int idSelectAll = wxNewId();
const int idSwapHeaderSource = wxNewId();
const int idOpenContainingFolder = wxNewId();
const int idBookmarks = wxNewId();
const int idBookmarksToggle = wxNewId();
const int idBookmarksPrevious = wxNewId();
const int idBookmarksNext = wxNewId();
const int idBookmarksClearAll = wxNewId();
const int idFolding = wxNewId();
const int idFoldingFoldAll = wxNewId();
const int idFoldingUnfoldAll = wxNewId();
const int idFoldingToggleAll = wxNewId();
const int idFoldingFoldCurrent = wxNewId();
const int idFoldingUnfoldCurrent = wxNewId();
const int idFoldingToggleCurrent = wxNewId();
const int idInsert = wxNewId();
const int idSplit = wxNewId();
const int idSplitHorz = wxNewId();
const int idSplitVert = wxNewId();
const int idUnsplit = wxNewId();
const int idProperties = wxNewId();
const int idAddFileToProject = wxNewId();
const int idRemoveFileFromProject = wxNewId();
const int idShowFileInProject = wxNewId();
const int idOpenUrl = wxNewId();
const int idBookmarkAdd = wxNewId();
const int idBookmarkRemove = wxNewId();
const int idBookmarkRemoveAll = wxNewId();
const int idBreakpointAdd = wxNewId();
const int idBreakpointEdit = wxNewId();
const int idBreakpointRemove = wxNewId();
const long idBreakpointEnable = wxNewId();
const long idBreakpointDisable = wxNewId();

BEGIN_EVENT_TABLE( M_Code_Editor, EditorBase )
  EVT_CLOSE( M_Code_Editor::OnClose )
  EVT_MENU( idUndo, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idRedo, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idClearHistory, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idCut, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idCopy, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idPaste, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idDelete, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idUpperCase, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idLowerCase, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idSelectAll, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idSwapHeaderSource, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idOpenContainingFolder, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarksToggle, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarksPrevious, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarksNext, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarksClearAll, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idFoldingFoldAll, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idFoldingUnfoldAll, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idFoldingToggleAll, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idFoldingFoldCurrent, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idFoldingUnfoldCurrent, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idFoldingToggleCurrent, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idProperties, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idAddFileToProject, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idRemoveFileFromProject, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idShowFileInProject, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarkAdd, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarkRemove, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBookmarkRemoveAll, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBreakpointAdd, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBreakpointEdit, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBreakpointRemove, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBreakpointEnable, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idBreakpointDisable, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idSplitHorz, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idSplitVert, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idUnsplit, M_Code_Editor::OnContextMenuEntry )
  EVT_MENU( idOpenUrl, M_Code_Editor::OnContextMenuEntry )
  EVT_SCI_ZOOM( -1, M_Code_Editor::OnZoom )
  EVT_SCI_ZOOM( -1, M_Code_Editor::OnZoom )
END_EVENT_TABLE()

struct M_Code_EditorInternalData {
  M_Code_Editor* m_pOwner;
  M_Code_EditorInternalData( M_Code_Editor* owner, LoaderBase* fileLoader = nullptr )
    : m_pOwner( owner ),
      m_ensure_final_line_end( false ),
      m_ensure_consistent_line_ends( true ),
      m_LastMarginMenuLine( -1 ),
      m_LastDebugLine( -1 ),
      m_useByteOrderMark( false ),
      m_byteOrderMarkLength( 0 ),
      m_lineNumbersWidth( 0 ),
      m_lineNumbersWidth2( 0 ),
      m_pFileLoader( fileLoader ) {
    m_encoding = wxLocale::GetSystemEncoding();
    if( m_pFileLoader ) {
      #ifdef fileload_measuring
      wxStopWatch sw;
      #endif
      EncodingDetector enc( fileLoader );
      if( enc.IsOK() ) {
        m_byteOrderMarkLength = enc.GetBOMSizeInBytes();
        m_useByteOrderMark = enc.UsesBOM();
        m_encoding = enc.GetFontEncoding();
      }
      #ifdef fileload_measuring
      Manager::Get()->GetLogManager()->DebugLog( F( _T( "Encoding via fileloader took : %d ms" ), ( int )sw.Time() ) );
      #endif
    }
  }

  ~M_Code_EditorInternalData() {
    if( m_pFileLoader ) {
      delete m_pFileLoader;
      m_pFileLoader = nullptr;
    }
  }

  void StripTrailingSpaces() {
    M_Code_StyledTextCtrl* control = m_pOwner->GetControl();
    int maxLines = control->GetLineCount();
    for( int line = 0; line < maxLines; line++ ) {
      int lineStart = control->PositionFromLine( line );
      int lineEnd = control->GetLineEndPosition( line );
      int i = lineEnd - 1;
      wxChar ch = ( wxChar )( control->GetCharAt( i ) );
      if( control->GetLexer() == wxSCI_LEX_DIFF ) {
        lineStart++;
      }
      while( ( i >= lineStart ) && ( ( ch == _T( ' ' ) ) || ( ch == _T( '\t' ) ) ) ) {
        i--;
        ch = ( wxChar )( control->GetCharAt( i ) );
      }
      if( i < ( lineEnd - 1 ) ) {
        control->SetTargetStart( i + 1 );
        control->SetTargetEnd( lineEnd );
        control->ReplaceTarget( _T( "" ) );
      }
    }
  }

  void EnsureFinalLineEnd() {
    M_Code_StyledTextCtrl* control = m_pOwner->GetControl();
    int maxLines = control->GetLineCount();
    int enddoc = control->PositionFromLine( maxLines );
    if( maxLines <= 1 || enddoc > control->PositionFromLine( maxLines - 1 ) ) {
      control->InsertText( enddoc, _T( "\n" ) );
    }
  }

  void EnsureConsistentLineEnds() {
    M_Code_StyledTextCtrl* control = m_pOwner->GetControl();
    control->ConvertEOLs( control->GetEOLMode() );
  }

  void SetLineNumberColWidth( bool both = true ) {
    ConfigManager* cfg = Manager::Get()->GetConfigManager( _T( "editor" ) );
    if( cfg->ReadBool( _T( "/show_line_numbers" ), true ) ) {
      if( m_pOwner->m_pControl2 && both ) {
        int pixelWidth = m_pOwner->m_pControl->TextWidth( wxSCI_STYLE_LINENUMBER, _T( "9" ) );
        int pixelWidth2 = m_pOwner->m_pControl2->TextWidth( wxSCI_STYLE_LINENUMBER, _T( "9" ) );
        if( cfg->ReadBool( _T( "/margin/dynamic_width" ), false ) ) {
          int lineNumChars = 1;
          int lineCount = m_pOwner->m_pControl->GetLineCount();
          while( lineCount >= 10 ) {
            lineCount /= 10;
            ++lineNumChars;
          }
          int lineNumWidth =  lineNumChars * pixelWidth + pixelWidth * 0.75;
          if( lineNumWidth != m_lineNumbersWidth ) {
            m_pOwner->m_pControl->SetMarginWidth( C_LINE_MARGIN, lineNumWidth );
            m_lineNumbersWidth = lineNumWidth;
          }
          lineNumWidth =  lineNumChars * pixelWidth2 + pixelWidth2 * 0.75;
          if( lineNumWidth != m_lineNumbersWidth2 ) {
            m_pOwner->m_pControl2->SetMarginWidth( C_LINE_MARGIN, lineNumWidth );
            m_lineNumbersWidth2 = lineNumWidth;
          }
        } else {
          m_pOwner->m_pControl->SetMarginWidth( C_LINE_MARGIN, pixelWidth * 0.75 + cfg->ReadInt( _T( "/margin/width_chars" ), 6 ) * pixelWidth );
          m_pOwner->m_pControl2->SetMarginWidth( C_LINE_MARGIN, pixelWidth * 0.75 + cfg->ReadInt( _T( "/margin/width_chars" ), 6 ) * pixelWidth );
        }
      } else {
        M_Code_StyledTextCtrl* control = m_pOwner->GetControl();
        int* pLineNumbersWidth = nullptr;
        if( control == m_pOwner->m_pControl ) {
          pLineNumbersWidth = &m_lineNumbersWidth;
        } else
        { pLineNumbersWidth = &m_lineNumbersWidth2; }
        int pixelWidth = control->TextWidth( wxSCI_STYLE_LINENUMBER, _T( "9" ) );
        if( cfg->ReadBool( _T( "/margin/dynamic_width" ), false ) ) {
          int lineNumChars = 1;
          int lineCount = control->GetLineCount();
          while( lineCount >= 10 ) {
            lineCount /= 10;
            ++lineNumChars;
          }
          int lineNumWidth =  lineNumChars * pixelWidth + pixelWidth * 0.75;
          if( lineNumWidth != *pLineNumbersWidth ) {
            control->SetMarginWidth( C_LINE_MARGIN, lineNumWidth );
            *pLineNumbersWidth = lineNumWidth;
          }
        } else
        { control->SetMarginWidth( C_LINE_MARGIN, pixelWidth * 0.75 + cfg->ReadInt( _T( "/margin/width_chars" ), 6 ) * pixelWidth ); }
      }
    } else {
      m_pOwner->m_pControl->SetMarginWidth( C_LINE_MARGIN, 0 );
      if( m_pOwner->m_pControl2 && both ) {
        m_pOwner->m_pControl2->SetMarginWidth( C_LINE_MARGIN, 0 );
      }
    }
  }

  void SetFoldingColWidth( bool both = true ) {
    float pointSize = m_pOwner->m_pControl->StyleGetFont( wxSCI_STYLE_DEFAULT ).GetPointSize();
    if( both ) {
      int width = 16 * ( pointSize + m_pOwner->m_pControl->GetZoom() ) / pointSize;
      if( width < 1 ) {
        width = 1;
      }
      m_pOwner->m_pControl->SetMarginWidth( C_FOLDING_MARGIN, width );
      if( m_pOwner->m_pControl2 ) {
        width = 16 * ( pointSize + m_pOwner->m_pControl2->GetZoom() ) / pointSize;
        if( width < 1 ) {
          width = 1;
        }
        m_pOwner->m_pControl2->SetMarginWidth( C_FOLDING_MARGIN, width );
      }
    } else {
      int width = 16 * ( pointSize + m_pOwner->GetControl()->GetZoom() ) / pointSize;
      if( width < 1 ) {
        width = 1;
      }
      m_pOwner->GetControl()->SetMarginWidth( C_FOLDING_MARGIN, width );
    }
  }

  wxString GetUrl() {
    M_Code_StyledTextCtrl* control = m_pOwner->GetControl();
    if( !control ) {
      return wxEmptyString;
    }
    wxRegEx reUrl( _T( "***:("
                       "((ht|f)tp(s?)\\:\\/\\/)"
                       "|(www\\.)"
                       ")"
                       "("
                       "([\\w\\-]+(\\.[\\w\\-]+)+)"
                       "|localhost"
                       ")"
                       "(\\/?)([\\w\\-\\.\\?\\,\\'\\/\\\\\\+&amp;%\\$#]*)?"
                       "([\\d\\w\\.\\/\\%\\+\\-\\=\\&amp;\\?\\:\\\\\\&quot;\\'\\,\\|\\~\\;]*)" ) );
    wxString url = control->GetSelectedText();
    if( reUrl.Matches( url ) ) {
      return reUrl.GetMatch( url );
    }
    int startPos = control->GetCurrentPos();
    const wxString space = _T( " \n\r\t{}" );
    wxChar curCh = control->GetCharAt( startPos );
    while( ( startPos > 0 ) && ( space.Find( curCh ) == -1 ) ) {
      startPos--;
      curCh = control->GetCharAt( startPos );
    }
    int endPos = control->GetCurrentPos();
    int maxPos = control->GetLineEndPosition( control->GetLineCount() );
    curCh = control->GetCharAt( endPos );
    while( ( endPos < maxPos ) && ( space.Find( curCh ) == -1 ) ) {
      endPos++;
      curCh = control->GetCharAt( endPos );
    }
    url = control->GetTextRange( startPos, endPos );
    if( ( control->GetLexer() == wxSCI_LEX_CPP ) && ( ( control->GetStyleAt( control->GetCurrentPos() ) == wxSCI_C_STRING )
        || ( control->GetStyleAt( control->GetCurrentPos() ) == wxSCI_C_STRINGEOL ) ) ) {
      url.Replace( _T( "\\n" ), _T( "\n" ) );
      url.Replace( _T( "\\r" ), _T( "\r" ) );
      url.Replace( _T( "\\t" ), _T( "\t" ) );
    }
    if( reUrl.Matches( url ) ) {
      wxString match = reUrl.GetMatch( url );
      if( ( url.Find( match ) + startPos < control->GetCurrentPos() ) && ( url.Find( match ) + startPos + ( int )match.Length() > control->GetCurrentPos() ) ) {
        url = match( 0, match.find_last_not_of( _T( ",." ) ) + 1 );
      } else
      { url = wxEmptyString; }
    } else
    { url = wxEmptyString; }
    return url;
  }

  static wxString GetLineIndentString( int line, M_Code_StyledTextCtrl* stc ) {
    int currLine = ( line == -1 ) ? stc->LineFromPosition( stc->GetCurrentPos() ) : line;
    wxString text = stc->GetLine( currLine );
    unsigned int len = text.Length();
    wxString indent;
    for( unsigned int i = 0; i < len; ++i ) {
      if( text[i] == _T( ' ' ) || text[i] == _T( '\t' ) ) {
        indent << text[i];
      } else
      { break; }
    }
    return indent;
  }
  static int DetectIndentStyle( M_Code_StyledTextCtrl* stc ) {
    int lineCount[] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    const int maxLine = std::min( stc->GetLineCount(), stc->GetLineCount() / 2 + 500 );
    for( int line = std::max( 0, stc->GetLineCount() / 2 - 500 ); line < maxLine; ++line ) {
      const wxString& indent = M_Code_EditorInternalData::GetLineIndentString( line, stc );
      if( indent.IsEmpty() ) {
        continue;
      }
      if( indent.Find( _T( '\t' ) ) != wxNOT_FOUND ) {
        ++lineCount[0];
      } else {
        ++lineCount[1];
        for( int i = 2; i < 9; ++i ) {
          if( indent.Length() % i == 0 ) {
            ++lineCount[i];
          }
        }
      }
    }
    if( lineCount[0] > 0 && lineCount[1] == 0 ) {
      return 0;
    }
    double total = lineCount[0] + lineCount[1];
    if( total < 10 ) {
      return -1;
    } else if( lineCount[0] / total > 0.75 ) {
      return 0;
    } else if( lineCount[1] / total < 0.75 ) {
      return -1;
    }
    total = lineCount[1];
    int tabSize = 8;
    for( int i = 2; i < 8; ++i ) {
      if( lineCount[i] > lineCount[tabSize] ) {
        tabSize = i;
      }
    }
    if( lineCount[tabSize] / total < 0.65 ) {
      return -1;
    }
    switch( tabSize ) {
      case 2:
        if( ( lineCount[2] - lineCount[6] ) / total < 0.1 ) {
          return 6;
        }
        if( lineCount[2] > lineCount[4] * 1.8
            || lineCount[4] / total < 0.5 ) {
          return 2;
        }
      case 4:
        if( lineCount[4] > lineCount[8] * 1.8
            || lineCount[8] / total < 0.5 ) {
          return 4;
        }
      case 8:
        if( lineCount[8] / total < 0.6 ) {
          return -1;
        }
        return 8;
      case 3:
        if( lineCount[3] > lineCount[6] * 1.8
            || lineCount[6] / total < 0.5 ) {
          return 3;
        }
        if( lineCount[6] / total < 0.6 ) {
          return -1;
        }
        return 6;
      default:
        if( lineCount[tabSize] / total < 0.7 ) {
          return -1;
        }
        return tabSize;
    }
  }
  bool m_ensure_final_line_end;
  bool m_ensure_consistent_line_ends;
  int m_LastMarginMenuLine;
  int m_LastDebugLine;
  bool mFoldingLimit;
  int mFoldingLimitLevel;
  wxFontEncoding m_encoding;
  bool m_useByteOrderMark;
  int m_byteOrderMarkLength;
  int m_lineNumbersWidth;
  int m_lineNumbersWidth2;
  LoaderBase* m_pFileLoader;
};

M_Code_Editor::M_Code_Editor( wxWindow* parent, const wxString& filename, EditorColourSet* theme )
  : EditorBase( parent, filename ),
    m_pSplitter( nullptr ),
    m_pSizer( nullptr ),
    m_pControl( nullptr ),
    m_pControl2( nullptr ),
    m_foldBackup( nullptr ),
    m_SplitType( stNoSplit ),
    m_Modified( false ),
    m_Index( -1 ),
    m_pProjectFile( nullptr ),
    m_pTheme( theme ),
    m_lang( HL_AUTO ) {
  DoInitializations( filename );
}

M_Code_Editor::M_Code_Editor( wxWindow* parent, LoaderBase* fileLdr, const wxString& filename, EditorColourSet* theme )
  : EditorBase( parent, filename ),
    m_pSplitter( nullptr ),
    m_pSizer( nullptr ),
    m_pControl( nullptr ),
    m_pControl2( nullptr ),
    m_foldBackup( nullptr ),
    m_SplitType( stNoSplit ),
    m_Modified( false ),
    m_Index( -1 ),
    m_pProjectFile( nullptr ),
    m_pTheme( theme ),
    m_lang( HL_AUTO ) {
  DoInitializations( filename, fileLdr );
}

M_Code_Editor::~M_Code_Editor() {
  SetSizer( nullptr );
  UpdateProjectFile();
  if( m_pControl ) {
    if( m_pProjectFile ) {
      m_pProjectFile->editorOpen = false;
    }
    m_pControl->Destroy();
    m_pControl = nullptr;
  }
  DestroySplitView();
  delete m_pData;
}

void M_Code_Editor::DoInitializations( const wxString& filename, LoaderBase* fileLdr ) {
  m_pData = new M_Code_EditorInternalData( this );
  m_pData->m_pFileLoader = fileLdr;
  m_IsBuiltinEditor = true;
  if( !filename.IsEmpty() ) {
    InitFilename( filename );
    wxFileName fname( m_Filename );
    NormalizePath( fname, wxEmptyString );
    m_Filename = fname.GetFullPath();
  } else {
    static int untitledCounter = 1;
    wxString f;
    M_Code_Project* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
    if( prj ) {
      f.Printf( _T( "%sUntitled%d" ), prj->GetBasePath().c_str(), untitledCounter++ );
    } else
    { f.Printf( _T( "Untitled%d" ), untitledCounter++ ); }
    InitFilename( f );
  }
  m_pSizer = new wxBoxSizer( wxVERTICAL );
  m_pControl = CreateEditor();
  m_pSizer->Add( m_pControl, 1, wxEXPAND );
  SetSizer( m_pSizer );
  m_pSizer->SetItemMinSize( m_pControl, 32, 32 );
  m_pControl->SetMarginMask( C_LINE_MARGIN, 0 );
  m_pControl->SetMarginMask( C_MARKER_MARGIN, 0 );
  m_pControl->SetMarginMask( C_CHANGEBAR_MARGIN, 0 );
  m_pControl->SetMarginMask( C_FOLDING_MARGIN, 0 );
  SetEditorStyleBeforeFileOpen();
  m_IsOK = Open();
  SetEditorStyleAfterFileOpen();
  if( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/folding/fold_all_on_open" ), false ) ) {
    FoldAll();
  }
  if( !m_IsOK || filename.IsEmpty() ) {
    SetModified( true );
    m_IsOK = false;
  }
  ConnectEvents( m_pControl );
}

void M_Code_Editor::NotifyPlugins( wxEventType type, int intArg, const wxString& strArg, int xArg, int yArg ) {
  if( !Manager::Get()->GetPluginManager() ) {
    return;
  }
  CodeBlocksEvent event( type );
  event.SetEditor( this );
  event.SetInt( intArg );
  event.SetString( strArg );
  event.SetX( xArg );
  event.SetY( yArg );
  if( m_pProjectFile ) {
    event.SetProject( m_pProjectFile->GetParentProject() );
  }
  Manager::Get()->GetPluginManager()->NotifyPlugins( event );
}

void M_Code_Editor::DestroySplitView() {
  if( m_pControl2 ) {
    m_pControl2->Destroy();
    m_pControl2 = nullptr;
  }
  if( m_pSplitter ) {
    m_pSplitter->Destroy();
    m_pSplitter = nullptr;
  }
}

M_Code_StyledTextCtrl* M_Code_Editor::GetControl() const {
  if( m_pControl2 ) {
    if( m_pControl2->GetLastFocusTime() > m_pControl->GetLastFocusTime() ) {
      return m_pControl2;
    }
  }
  return m_pControl;
}

bool M_Code_Editor::GetModified() const {
  return m_Modified || m_pControl->GetModify();
}

void M_Code_Editor::SetModified( bool modified ) {
  if( modified != m_Modified ) {
    m_Modified = modified;
    if( !m_Modified ) {
      m_pControl->SetSavePoint();
    }
    SetEditorTitle( m_Shortname );
    NotifyPlugins( cbEVT_EDITOR_MODIFIED );
    if( m_pProjectFile ) {
      m_pProjectFile->SetFileState( m_pControl->GetReadOnly() ? fvsReadOnly : ( m_Modified ? fvsModified : fvsNormal ) );
    }
  }
}

void M_Code_Editor::SetEditorTitle( const wxString& title ) {
  if( m_Modified ) {
    SetTitle( g_EditorModified + title );
  } else {
    SetTitle( title );
  }
}

void M_Code_Editor::SetProjectFile( ProjectFile* project_file, bool preserve_modified ) {
  if( m_pProjectFile == project_file ) {
    return;
  }
  bool wasmodified = false;
  if( preserve_modified ) {
    wasmodified = GetModified();
  }
  m_pProjectFile = project_file;
  if( m_pProjectFile ) {
    m_Filename = UnixFilename( project_file->file.GetFullPath() );
    m_pControl->GotoPos( m_pProjectFile->editorPos );
    m_pControl->ScrollToLine( m_pProjectFile->editorTopLine );
    m_pControl->ScrollToColumn( 0 );
    m_pControl->SetZoom( m_pProjectFile->editorZoom );
    if( m_pProjectFile->editorSplit != ( int )stNoSplit ) {
      Split( ( SplitType )m_pProjectFile->editorSplit );
      if( m_pControl2 ) {
        m_pSplitter->SetSashPosition( m_pProjectFile->editorSplitPos );
        m_pControl2->GotoPos( m_pProjectFile->editorPos_2 );
        m_pControl2->ScrollToLine( m_pProjectFile->editorTopLine_2 );
        m_pControl2->ScrollToColumn( 0 );
        m_pControl2->SetZoom( m_pProjectFile->editorZoom_2 );
      }
    }
    m_pData->SetLineNumberColWidth();
    if( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/folding/show_folds" ), true ) ) {
      for( unsigned int i = 0; i < m_pProjectFile->editorFoldLinesArray.GetCount(); i++ ) {
        m_pControl->ToggleFold( m_pProjectFile->editorFoldLinesArray[i] );
      }
    }
    m_pProjectFile->editorOpen = true;
    if( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/tab_text_relative" ), true ) ) {
      m_Shortname = m_pProjectFile->relativeToCommonTopLevelPath;
    } else
    { m_Shortname = m_pProjectFile->file.GetFullName(); }
    SetEditorTitle( m_Shortname );
    if( !wxFileExists( m_Filename ) ) {
      m_pProjectFile->SetFileState( fvsMissing );
    } else if( !wxFile::Access( m_Filename.c_str(), wxFile::write ) ) {
      m_pProjectFile->SetFileState( fvsReadOnly );
    }
  }
  if( preserve_modified ) {
    SetModified( wasmodified );
  }
}

void M_Code_Editor::UpdateProjectFile() {
  if( m_pControl && m_pProjectFile ) {
    m_pProjectFile->editorOpen = true;
    m_pProjectFile->editorSplit = m_SplitType;
    m_pProjectFile->editorPos = m_pControl->GetCurrentPos();
    m_pProjectFile->editorTopLine = m_pControl->GetFirstVisibleLine();
    m_pProjectFile->editorZoom = m_pControl->GetZoom();
    m_pProjectFile->editorSplitActive = 1;
    if( m_pControl2 ) {
      m_pProjectFile->editorSplitPos = m_pSplitter->GetSashPosition();
      m_pProjectFile->editorPos_2 = m_pControl2->GetCurrentPos();
      m_pProjectFile->editorTopLine_2 = m_pControl2->GetFirstVisibleLine();
      m_pProjectFile->editorZoom_2 = m_pControl2->GetZoom();
      if( GetControl() == m_pControl2 ) {
        m_pProjectFile->editorSplitActive = 2;
      }
    }
    if( m_pProjectFile->editorFoldLinesArray.GetCount() != 0 ) {
      m_pProjectFile->editorFoldLinesArray.Clear();
    }
    int i = 0;
    while( ( i = m_pControl->ContractedFoldNext( i ) ) != -1 ) {
      m_pProjectFile->editorFoldLinesArray.Add( i++ );
    }
  }
}

void M_Code_Editor::SetMarkerStyle( int marker, int markerType, wxColor fore, wxColor back ) {
  m_pControl->MarkerDefine( marker, markerType );
  m_pControl->MarkerSetForeground( marker, fore );
  m_pControl->MarkerSetBackground( marker, back );
  if( m_pControl2 ) {
    m_pControl2->MarkerDefine( marker, markerType );
    m_pControl2->MarkerSetForeground( marker, fore );
    m_pControl2->MarkerSetBackground( marker, back );
  }
}

void M_Code_Editor::UnderlineFoldedLines( bool underline ) {
  m_pControl->SetFoldFlags( underline ? 16 : 0 );
  if( m_pControl2 ) {
    m_pControl2->SetFoldFlags( underline ? 16 : 0 );
  }
}

M_Code_StyledTextCtrl* M_Code_Editor::CreateEditor() {
  wxSize size = m_pControl ? wxDefaultSize : GetSize();
  size.x = std::max( size.x, -1 );
  size.y = std::max( size.y, -1 );
  M_Code_StyledTextCtrl* control = new M_Code_StyledTextCtrl( this, wxNewId(), wxDefaultPosition, size );
  control->UsePopUp( false );
  m_pData->m_encoding = wxFONTENCODING_UTF8;
  for( int marker = 0 ; marker <= wxSCI_MARKNUM_LASTUNUSED ; ++marker ) {
    control->MarkerDefine( marker, wxSCI_MARK_EMPTY );
  }
  return control;
}

void M_Code_Editor::ConnectEvents( M_Code_StyledTextCtrl* stc ) {
  wxWindowID stcID = stc->GetId();
  Connect( stcID, wxEVT_SCI_MARGINCLICK, wxScintillaEventHandler( M_Code_Editor::OnMarginClick ) );
  Connect( stcID, wxEVT_SCI_UPDATEUI, wxScintillaEventHandler( M_Code_Editor::OnEditorUpdateUI ) );
  Connect( stcID, wxEVT_SCI_CHANGE, wxScintillaEventHandler( M_Code_Editor::OnEditorChange ) );
  Connect( stcID, wxEVT_SCI_CHARADDED, wxScintillaEventHandler( M_Code_Editor::OnEditorCharAdded ) );
  Connect( stcID, wxEVT_SCI_DWELLSTART, wxScintillaEventHandler( M_Code_Editor::OnEditorDwellStart ) );
  Connect( stcID, wxEVT_SCI_DWELLEND, wxScintillaEventHandler( M_Code_Editor::OnEditorDwellEnd ) );
  Connect( stcID, wxEVT_SCI_USERLISTSELECTION, wxScintillaEventHandler( M_Code_Editor::OnUserListSelection ) );
  Connect( stcID, wxEVT_SCI_MODIFIED, wxScintillaEventHandler( M_Code_Editor::OnEditorModified ) );
  int scintilla_events[] = {
    wxEVT_SCI_STYLENEEDED,
    wxEVT_SCI_SAVEPOINTREACHED,
    wxEVT_SCI_SAVEPOINTLEFT,
    wxEVT_SCI_ROMODIFYATTEMPT,
    wxEVT_SCI_KEY,
    wxEVT_SCI_DOUBLECLICK,
    wxEVT_SCI_MACRORECORD,
    wxEVT_SCI_NEEDSHOWN,
    wxEVT_SCI_PAINTED,
    wxEVT_SCI_URIDROPPED,
    wxEVT_SCI_START_DRAG,
    wxEVT_SCI_FINISHED_DRAG,
    wxEVT_SCI_DRAG_OVER,
    wxEVT_SCI_DO_DROP,
    wxEVT_SCI_ZOOM,
    wxEVT_SCI_HOTSPOT_CLICK,
    wxEVT_SCI_HOTSPOT_DCLICK,
    wxEVT_SCI_CALLTIP_CLICK,
    wxEVT_SCI_AUTOCOMP_SELECTION,
    wxEVT_SCI_AUTOCOMP_CANCELLED,
    wxEVT_SCI_TAB,
    wxEVT_SCI_ESC,
    -1
  };
  int i = 0;
  while( scintilla_events[i] != -1 ) {
    Connect( stcID, scintilla_events[i], wxScintillaEventHandler( M_Code_Editor::OnScintillaEvent ) );
    ++i;
  }
}

void M_Code_Editor::Split( M_Code_Editor::SplitType split ) {
  Freeze();
  if( m_pSplitter ) {
    Unsplit();
    Manager::Yield();
  }
  m_SplitType = split;
  if( m_SplitType == stNoSplit ) {
    Thaw();
    return;
  }
  m_pSizer->Detach( m_pControl );
  m_pSplitter = new wxSplitterWindow( this, wxNewId(), wxDefaultPosition, wxDefaultSize, wxSP_NOBORDER | wxSP_LIVE_UPDATE );
  m_pSplitter->SetMinimumPaneSize( 32 );
  m_pControl2 = CreateEditor();
  InternalSetEditorStyleBeforeFileOpen( m_pControl2 );
  for( int i = 0; i < wxSCI_INDIC_MAX; ++i ) {
    m_pControl2->IndicatorSetStyle( i, m_pControl->IndicatorGetStyle( i ) );
    m_pControl2->IndicatorSetUnder( i, m_pControl->IndicatorGetUnder( i ) );
    m_pControl2->IndicatorSetForeground( i, m_pControl->IndicatorGetForeground( i ) );
  }
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
  SetFoldingIndicator();
  UnderlineFoldedLines( mgr->ReadBool( _T( "/folding/underline_folded_line" ), true ) );
  if( m_pTheme ) {
    m_pTheme->Apply( m_lang, m_pControl2 );
  }
  m_pControl2->SetDocPointer( m_pControl->GetDocPointer() );
  m_pControl->Reparent( m_pSplitter );
  m_pControl2->Reparent( m_pSplitter );
  m_pSizer->SetDimension( 0, 0, GetSize().x, GetSize().y );
  m_pSizer->Add( m_pSplitter, 1, wxEXPAND );
  m_pSizer->Layout();
  switch( m_SplitType ) {
    case stHorizontal:
      m_pSplitter->SplitHorizontally( m_pControl, m_pControl2, 0 );
      break;
    case stVertical:
      m_pSplitter->SplitVertically( m_pControl, m_pControl2, 0 );
      break;
    case stNoSplit:
    default:
      break;
  }
  SetEditorStyleAfterFileOpen();
  m_pControl2->SetZoom( m_pControl->GetZoom() );
  m_pControl2->SetMarginWidth( C_LINE_MARGIN, m_pControl->GetMarginWidth( C_LINE_MARGIN ) );
  ConnectEvents( m_pControl2 );
  NotifyPlugins( cbEVT_EDITOR_SPLIT );
  Thaw();
}

void M_Code_Editor::Unsplit() {
  m_SplitType = stNoSplit;
  if( !m_pSplitter ) {
    return;
  }
  Freeze();
  if( GetControl() == m_pControl2 ) {
    M_Code_StyledTextCtrl* tmp = m_pControl;
    m_pControl = m_pControl2;
    m_pControl2 = tmp;
  }
  m_pSizer->Detach( m_pSplitter );
  m_pControl->Reparent( this );
  m_pSizer->Add( m_pControl, 1, wxEXPAND );
  NotifyPlugins( cbEVT_EDITOR_UNSPLIT );
  DestroySplitView();
  m_pSizer->Layout();
  Thaw();
}

wxColour M_Code_Editor::GetOptionColour( const wxString& option, const wxColour _default ) {
  return Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadColour( option, _default );
}

void M_Code_Editor::SetEditorStyle() {
  SetEditorStyleBeforeFileOpen();
  SetEditorStyleAfterFileOpen();
}

inline void OverrideUseTabsPerLanguage( M_Code_StyledTextCtrl *control ) {
  if( !control ) {
    return;
  }
  int lexer = control->GetLexer();
  switch( lexer ) {
    case wxSCI_LEX_PYTHON:
      control->SetUseTabs( false );
      break;
    case wxSCI_LEX_MAKEFILE:
      control->SetUseTabs( true );
      break;
    default:
      break;
  }
}

void M_Code_Editor::SetEditorStyleBeforeFileOpen() {
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
  if( m_pProjectFile ) {
    if( mgr->ReadBool( _T( "/tab_text_relative" ), true ) ) {
      m_Shortname = m_pProjectFile->relativeToCommonTopLevelPath;
    } else
    { m_Shortname = m_pProjectFile->file.GetFullName(); }
    SetEditorTitle( m_Shortname );
  }
  m_pData->mFoldingLimit = mgr->ReadBool( _T( "/folding/limit" ), false );
  m_pData->mFoldingLimitLevel = mgr->ReadInt( _T( "/folding/limit_level" ), 1 );
  m_pData->m_ensure_final_line_end = mgr->ReadBool( _T( "/eol/ensure_final_line_end" ), true );
  m_pData->m_ensure_consistent_line_ends = mgr->ReadBool( _T( "/eol/ensure_consistent_line_ends" ), false );
  InternalSetEditorStyleBeforeFileOpen( m_pControl );
  if( m_pControl2 ) {
    InternalSetEditorStyleBeforeFileOpen( m_pControl2 );
  }
  SetFoldingIndicator();
  SetLanguage( HL_AUTO );
  OverrideUseTabsPerLanguage( m_pControl );
  OverrideUseTabsPerLanguage( m_pControl2 );
}

void M_Code_Editor::SetEditorStyleAfterFileOpen() {
  InternalSetEditorStyleAfterFileOpen( m_pControl );
  if( m_pControl2 ) {
    InternalSetEditorStyleAfterFileOpen( m_pControl2 );
  }
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
  UnderlineFoldedLines( mgr->ReadBool( _T( "/folding/underline_folded_line" ), true ) );
  m_pData->SetLineNumberColWidth();
}


void M_Code_Editor::ApplyStyles( M_Code_StyledTextCtrl* control ) {
  if( !control ) {
    return;
  }
  InternalSetEditorStyleBeforeFileOpen( control );
  InternalSetEditorStyleAfterFileOpen( control );
}

void M_Code_Editor::InternalSetEditorStyleBeforeFileOpen( M_Code_StyledTextCtrl* control ) {
  if( !control ) {
    return;
  }
  control->Colourise( 0, -1 );
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
  wxFont font( 10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL );
  wxString fontstring = mgr->Read( _T( "/font" ), wxEmptyString );
  if( !fontstring.IsEmpty() ) {
    wxNativeFontInfo nfi;
    nfi.FromString( fontstring );
    font.SetNativeFontInfo( nfi );
  }
  control->SetMouseDwellTime( 1000 );
  int caretStyle = mgr->ReadInt( _T( "/caret/style" ), wxSCI_CARETSTYLE_LINE );
  control->SetCaretStyle( caretStyle );
  if( caretStyle == wxSCI_CARETSTYLE_LINE ) {
    control->SetCaretWidth( mgr->ReadInt( _T( "/caret/width" ), 2 ) );
  } else {
    control->SetCaretWidth( 1 );
  }
  ColourManager *colours = Manager::Get()->GetColourManager();
  control->SetCaretForeground( colours->GetColour( _T( "editor_caret" ) ) );
  control->SetCaretPeriod( mgr->ReadInt( _T( "/caret/period" ), 500 ) );
  control->SetCaretLineVisible( mgr->ReadBool( _T( "/highlight_caret_line" ), false ) );
  control->SetCaretLineBackground( GetOptionColour( _T( "/highlight_caret_line_colour" ), wxColour( 0xFF, 0xFF, 0x00 ) ) );
  control->SetFoldMarginColour( true, colours->GetColour( _T( "editor_margin_chrome" ) ) );
  control->SetFoldMarginHiColour( true, colours->GetColour( _T( "editor_margin_chrome_highlight" ) ) );
  control->SetWhitespaceForeground( true, colours->GetColour( _T( "editor_whitespace" ) ) );
  if( mgr->ReadBool( _T( "/camel_case" ), false ) ) {
    control->CmdKeyAssign( wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL, wxSCI_CMD_WORDPARTLEFT );
    control->CmdKeyAssign( wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL, wxSCI_CMD_WORDPARTRIGHT );
    control->CmdKeyAssign( wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL | wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDPARTLEFTEXTEND );
    control->CmdKeyAssign( wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL | wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDPARTRIGHTEXTEND );
  } else {
    control->CmdKeyAssign( wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL, wxSCI_CMD_WORDLEFT );
    control->CmdKeyAssign( wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL, wxSCI_CMD_WORDRIGHT );
    control->CmdKeyAssign( wxSCI_KEY_LEFT,  wxSCI_SCMOD_CTRL | wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDLEFTEXTEND );
    control->CmdKeyAssign( wxSCI_KEY_RIGHT, wxSCI_SCMOD_CTRL | wxSCI_SCMOD_SHIFT, wxSCI_CMD_WORDRIGHTEXTEND );
  }
  control->SetUseTabs( mgr->ReadBool( _T( "/use_tab" ), false ) );
  control->SetIndentationGuides( mgr->ReadBool( _T( "/show_indent_guides" ), false ) ? wxSCI_IV_LOOKBOTH : wxSCI_IV_NONE );
  control->SetTabIndents( mgr->ReadBool( _T( "/tab_indents" ), true ) );
  control->SetBackSpaceUnIndents( mgr->ReadBool( _T( "/backspace_unindents" ), true ) );
  control->SetWrapMode( mgr->ReadBool( _T( "/word_wrap" ), false ) );
  if( mgr->ReadBool( _T( "/word_wrap_style_home_end" ), true ) ) {
    control->CmdKeyAssign( wxSCI_KEY_END,  wxSCI_SCMOD_NORM, wxSCI_CMD_LINEENDWRAP );
    control->CmdKeyAssign( wxSCI_KEY_END,  wxSCI_SCMOD_ALT, wxSCI_CMD_LINEEND );
    control->CmdKeyAssign( wxSCI_KEY_END,  wxSCI_SCMOD_SHIFT, wxSCI_CMD_LINEENDWRAPEXTEND );
    control->CmdKeyAssign( wxSCI_KEY_END,  wxSCI_SCMOD_SHIFT | wxSCI_SCMOD_ALT, wxSCI_CMD_LINEENDEXTEND );
    if( mgr->ReadBool( _T( "/simplified_home" ), false ) ) {
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_NORM, wxSCI_CMD_HOMEWRAP );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_ALT, wxSCI_CMD_HOME );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_SHIFT, wxSCI_CMD_HOMEWRAPEXTEND );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_SHIFT | wxSCI_SCMOD_ALT, wxSCI_CMD_HOMEEXTEND );
    } else {
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_NORM, wxSCI_CMD_VCHOMEWRAP );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_ALT, wxSCI_CMD_VCHOME );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_SHIFT, wxSCI_CMD_VCHOMEWRAPEXTEND );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_SHIFT | wxSCI_SCMOD_ALT, wxSCI_CMD_VCHOMEEXTEND );
    }
  } else {
    control->CmdKeyAssign( wxSCI_KEY_END,  wxSCI_SCMOD_ALT, wxSCI_CMD_LINEENDWRAP );
    control->CmdKeyAssign( wxSCI_KEY_END,  wxSCI_SCMOD_SHIFT | wxSCI_SCMOD_ALT, wxSCI_CMD_LINEENDWRAPEXTEND );
    if( mgr->ReadBool( _T( "/simplified_home" ), false ) ) {
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_ALT, wxSCI_CMD_HOMEWRAP );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_SHIFT | wxSCI_SCMOD_ALT, wxSCI_CMD_HOMEWRAPEXTEND );
    } else {
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_ALT, wxSCI_CMD_VCHOMEWRAP );
      control->CmdKeyAssign( wxSCI_KEY_HOME, wxSCI_SCMOD_SHIFT | wxSCI_SCMOD_ALT, wxSCI_CMD_VCHOMEWRAPEXTEND );
    }
  }
  control->SetViewEOL( mgr->ReadBool( _T( "/show_eol" ), false ) );
  control->SetViewWhiteSpace( mgr->ReadInt( _T( "/view_whitespace" ), 0 ) );
  control->SetEdgeColour( Manager::Get()->GetColourManager()->GetColour( _T( "editor_gutter" ) ) );
  int column = mgr->ReadInt( _T( "/gutter/column" ), 0 );
  if( column ) {
    control->SetEdgeColumn( mgr->ReadInt( _T( "/gutter/column" ), 0 ) );
    control->SetEdgeMode( 1 );
  }
  control->StyleSetFont( wxSCI_STYLE_DEFAULT, font );
  control->StyleClearAll();
  control->SetTabWidth( mgr->ReadInt( _T( "/tab_size" ), 4 ) );
  control->SetMarginWidth( C_MARKER_MARGIN, 16 );
  control->SetMarginType( C_MARKER_MARGIN, wxSCI_MARGIN_SYMBOL );
  control->SetMarginSensitive( C_MARKER_MARGIN, mgr->ReadBool( _T( "/margin_1_sensitive" ), true ) );
  control->SetMarginMask( C_MARKER_MARGIN, control->GetMarginMask( C_MARKER_MARGIN )
                          | ( 1 << BOOKMARK_MARKER )
                          | ( 1 << BREAKPOINT_MARKER )
                          | ( 1 << BREAKPOINT_DISABLED_MARKER )
                          | ( 1 << BREAKPOINT_OTHER_MARKER )
                          | ( 1 << DEBUG_MARKER )
                          | ( 1 << DEBUG_MARKER_HIGHLIGHT )
                          | ( 1 << ERROR_MARKER ) );
  control->MarkerDefine( BOOKMARK_MARKER, BOOKMARK_STYLE );
  control->MarkerSetBackground( BOOKMARK_MARKER, wxColour( 0xA0, 0xA0, 0xFF ) );
  const wxString &basepath = ConfigManager::GetDataFolder() + _T( "/images/" );
  bool imageBP = mgr->ReadBool( _T( "/margin_1_image_bp" ), true );
  if( imageBP ) {
    wxBitmap iconBP = M_Code_LoadBitmap( basepath + _T( "breakpoint.png" ), wxBITMAP_TYPE_PNG );
    wxBitmap iconBPDis = M_Code_LoadBitmap( basepath + _T( "breakpoint_disabled.png" ), wxBITMAP_TYPE_PNG );
    wxBitmap iconBPOth = M_Code_LoadBitmap( basepath + _T( "breakpoint_other.png" ), wxBITMAP_TYPE_PNG );
    if( iconBP.IsOk() && iconBPDis.IsOk() && iconBPOth.IsOk() ) {
      control->MarkerDefineBitmap( BREAKPOINT_MARKER, iconBP );
      control->MarkerDefineBitmap( BREAKPOINT_DISABLED_MARKER, iconBPDis );
      control->MarkerDefineBitmap( BREAKPOINT_OTHER_MARKER, iconBPOth );
    } else
    { imageBP = false; }
  }
  if( !imageBP ) {
    control->MarkerDefine( BREAKPOINT_MARKER, BREAKPOINT_STYLE );
    control->MarkerSetBackground( BREAKPOINT_MARKER, wxColour( 0xFF, 0x00, 0x00 ) );
    control->MarkerDefine( BREAKPOINT_DISABLED_MARKER, BREAKPOINT_STYLE );
    control->MarkerSetBackground( BREAKPOINT_DISABLED_MARKER, wxColour( 0x90, 0x90, 0x90 ) );
    control->MarkerDefine( BREAKPOINT_OTHER_MARKER, BREAKPOINT_STYLE );
    control->MarkerSetBackground( BREAKPOINT_OTHER_MARKER, wxColour( 0x59, 0x74, 0x8e ) );
  }
  control->MarkerDefine( DEBUG_MARKER, DEBUG_STYLE );
  control->MarkerSetBackground( DEBUG_MARKER, wxColour( 0xFF, 0xFF, 0x00 ) );
  control->MarkerDefine( DEBUG_MARKER_HIGHLIGHT, DEBUG_STYLE_HIGHLIGHT );
  control->MarkerSetBackground( DEBUG_MARKER_HIGHLIGHT, control->GetCaretLineBackground() );
  control->MarkerDefine( ERROR_MARKER, ERROR_STYLE );
  control->MarkerSetBackground( ERROR_MARKER, wxColour( 0xFF, 0x00, 0x00 ) );
  if( mgr->ReadBool( _T( "/margin/use_changebar" ), true ) ) {
    control->SetMarginWidth( C_CHANGEBAR_MARGIN, 4 );
    control->SetMarginType( C_CHANGEBAR_MARGIN,  wxSCI_MARGIN_SYMBOL );
    control->SetMarginMask( C_CHANGEBAR_MARGIN, control->GetMarginMask( C_CHANGEBAR_MARGIN ) | ( 1 << wxSCI_MARKNUM_CHANGEUNSAVED ) | ( 1 << wxSCI_MARKNUM_CHANGESAVED ) );
    control->MarkerDefine( wxSCI_MARKNUM_CHANGEUNSAVED, wxSCI_MARK_LEFTRECT );
    control->MarkerSetBackground( wxSCI_MARKNUM_CHANGEUNSAVED, wxColour( 0xFF, 0xE6, 0x04 ) );
    control->MarkerDefine( wxSCI_MARKNUM_CHANGESAVED, wxSCI_MARK_LEFTRECT );
    control->MarkerSetBackground( wxSCI_MARKNUM_CHANGESAVED,   wxColour( 0x04, 0xFF, 0x50 ) );
  } else {
    control->SetMarginWidth( C_CHANGEBAR_MARGIN, 0 );
  }
  control->SetScrollWidthTracking( mgr->ReadBool( _T( "/margin/scroll_width_tracking" ), false ) );
  control->SetMultipleSelection( mgr->ReadBool( _T( "/selection/multi_select" ),       false ) );
  control->SetAdditionalSelectionTyping( mgr->ReadBool( _T( "/selection/multi_typing" ),       false ) );
  unsigned virtualSpace = 0;
  if( mgr->ReadBool( _T( "/selection/use_rect_vspace" ), false ) ) {
    virtualSpace |= wxSCI_SCVS_RECTANGULARSELECTION;
  }
  if( mgr->ReadBool( _T( "/selection/use_vspace" ), false ) ) {
    virtualSpace |= wxSCI_SCVS_USERACCESSIBLE;
  }
  if( !virtualSpace ) {
    virtualSpace = wxSCI_SCVS_NONE;
  }
  control->SetVirtualSpaceOptions( virtualSpace );
}

void M_Code_Editor::InternalSetEditorStyleAfterFileOpen( M_Code_StyledTextCtrl* control ) {
  if( !control ) {
    return;
  }
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
  control->SetEOLMode( wxSCI_EOL_LF );
  if( mgr->ReadBool( _T( "/detect_indent" ), false ) ) {
    int indentStyle = M_Code_EditorInternalData::DetectIndentStyle( control );
    if( indentStyle == 0 ) {
      control->SetUseTabs( true );
    } else if( indentStyle != -1 ) {
      control->SetUseTabs( false );
      control->SetTabWidth( indentStyle );
    }
  }
  control->SetProperty( _T( "lexer.cpp.track.preprocessor" ), mgr->ReadBool( _T( "/track_preprocessor" ), true ) ? _T( "1" ) : _T( "0" ) );
  if( mgr->ReadBool( _T( "/folding/show_folds" ), true ) ) {
    control->SetProperty( _T( "fold" ), _T( "1" ) );
    control->SetProperty( _T( "fold.html" ), mgr->ReadBool( _T( "/folding/fold_xml" ), true ) ? _T( "1" ) : _T( "0" ) );
    control->SetProperty( _T( "fold.comment" ), mgr->ReadBool( _T( "/folding/fold_comments" ), false ) ? _T( "1" ) : _T( "0" ) );
    control->SetProperty( _T( "fold.compact" ), _T( "0" ) );
    control->SetProperty( _T( "fold.preprocessor" ), mgr->ReadBool( _T( "/folding/fold_preprocessor" ), false ) ? _T( "1" ) : _T( "0" ) );
    control->SetFoldFlags( 16 );
    control->SetMarginType( C_FOLDING_MARGIN, wxSCI_MARGIN_SYMBOL );
    control->SetMarginWidth( C_FOLDING_MARGIN, 16 );
    control->SetMarginMask( C_FOLDING_MARGIN, control->GetMarginMask( C_FOLDING_MARGIN )
                            | ( wxSCI_MASK_FOLDERS - ( ( 1 << wxSCI_MARKNUM_CHANGEUNSAVED )
                                | ( 1 << wxSCI_MARKNUM_CHANGESAVED ) ) ) );
    control->SetMarginSensitive( C_FOLDING_MARGIN, 1 );
  } else {
    control->SetProperty( _T( "fold" ), _T( "0" ) );
    control->SetMarginWidth( C_FOLDING_MARGIN, 0 );
  }
  control->SetProperty( _T( "highlight.wxsmith" ), mgr->ReadBool( _T( "/highlight_wxsmith" ), true ) ? _T( "1" ) : _T( "0" ) );
  control->SetMarginType( C_LINE_MARGIN, wxSCI_MARGIN_NUMBER );
}

void M_Code_Editor::SetColourSet( EditorColourSet* theme ) {
  m_pTheme = theme;
  SetLanguage( m_lang );
}

wxFontEncoding M_Code_Editor::GetEncoding() const {
  if( !m_pData ) {
    return wxFONTENCODING_SYSTEM;
  }
  return m_pData->m_encoding;
}

wxString M_Code_Editor::GetEncodingName() const {
  return wxFontMapper::GetEncodingName( GetEncoding() );
}

void M_Code_Editor::SetEncoding( wxFontEncoding encoding ) {
  if( !m_pData ) {
    return;
  }
  if( encoding == wxFONTENCODING_SYSTEM ) {
    encoding = wxLocale::GetSystemEncoding();
  }
  if( encoding == m_pData->m_encoding ) {
    return;
  }
  m_pData->m_encoding = encoding;
  SetModified( true );
}

bool M_Code_Editor::GetUseBom() const {
  if( !m_pData ) {
    return false;
  }
  return m_pData->m_useByteOrderMark;
}

void M_Code_Editor::SetUseBom( bool bom ) {
  if( !m_pData ) {
    return;
  }
  if( bom == GetUseBom() ) {
    return;
  }
  m_pData->m_useByteOrderMark = bom;
  SetModified( true );
}

bool M_Code_Editor::Reload( bool detect_encoding ) {
  const int pos = m_pControl ? m_pControl->GetCurrentPos() : 0;
  const int pos2 = m_pControl2 ? m_pControl2->GetCurrentPos() : 0;
  if( !Open( detect_encoding ) ) {
    return false;
  }
  SetEditorStyleAfterFileOpen();
  if( m_pControl ) {
    m_pControl->GotoPos( pos );
  }
  if( m_pControl2 ) {
    m_pControl2->GotoPos( pos2 );
  }
  return true;
}

void M_Code_Editor::Touch() {
  m_LastModified = wxDateTime::Now();
}

void M_Code_Editor::SetLanguage( HighlightLanguage lang ) {
  if( m_pTheme ) {
    m_lang = m_pTheme->Apply( this, lang );
  } else {
    m_lang = HL_AUTO;
  }
}

bool M_Code_Editor::Open( bool detectEncoding ) {
  if( m_pProjectFile ) {
    if( !wxFileExists( m_Filename ) ) {
      m_pProjectFile->SetFileState( fvsMissing );
    } else if( !wxFile::Access( m_Filename.c_str(), wxFile::write ) ) {
      m_pProjectFile->SetFileState( fvsReadOnly );
    }
  }
  if( !wxFileExists( m_Filename ) ) {
    return false;
  }
  SetReadOnly( false );
  m_pControl->ClearAll();
  m_pControl->SetModEventMask( 0 );
  if( !m_pData ) {
    return false;
  }
  if( !m_pData->m_pFileLoader ) {
    m_pData->m_pFileLoader = Manager::Get()->GetFileManager()->Load( m_Filename, false );
  }
  EncodingDetector enc( ( wxByte* )m_pData->m_pFileLoader->GetData(), m_pData->m_pFileLoader->GetLength() );
  if( detectEncoding ) {
    m_pData->m_useByteOrderMark = enc.UsesBOM();
    m_pData->m_byteOrderMarkLength = enc.GetBOMSizeInBytes();
    m_pData->m_encoding = enc.GetFontEncoding();
    SetEncoding( enc.GetFontEncoding() );
    SetUseBom( m_pData->m_byteOrderMarkLength > 0 );
  }
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
  m_pControl->InsertText( 0, enc.GetWxStr() );
  m_pControl->EmptyUndoBuffer( mgr->ReadBool( _T( "/margin/use_changebar" ), true ) );
  m_pControl->SetModEventMask( wxSCI_MODEVENTMASKALL );
  bool read_only = !wxFile::Access( m_Filename.c_str(), wxFile::write );
  SetReadOnly( read_only );
  wxFileName fname( m_Filename );
  m_LastModified = fname.GetModificationTime();
  SetModified( false );
  NotifyPlugins( cbEVT_EDITOR_OPEN );
  if( m_pData->m_pFileLoader ) {
    delete m_pData->m_pFileLoader;
    m_pData->m_pFileLoader = nullptr;
  }
  return true;
}

bool M_Code_Editor::Save() {
  if( !GetModified() ) {
    return true;
  }
  int columnC = m_pControl->GetColumn( m_pControl->GetCurrentPos() );
  int columnA = m_pControl->GetColumn( m_pControl->GetAnchor() );
  m_pControl->BeginUndoAction();
  m_pData->StripTrailingSpaces();
  if( m_pData->m_ensure_consistent_line_ends ) {
    m_pData->EnsureConsistentLineEnds();
  }
  if( m_pData->m_ensure_final_line_end ) {
    m_pData->EnsureFinalLineEnd();
  }
  m_pControl->EndUndoAction();
  columnC -= m_pControl->GetColumn( m_pControl->GetCurrentPos() );
  columnA -= m_pControl->GetColumn( m_pControl->GetAnchor() );
  if( columnC > 0 ) {
    m_pControl->SetSelectionNCaretVirtualSpace( 0,  columnC );
  }
  if( columnA > 0 ) {
    m_pControl->SetSelectionNAnchorVirtualSpace( 0, columnA );
  }
  if( !m_IsOK ) {
    return SaveAs();
  }
  m_pControl->BeginUndoAction();
  NotifyPlugins( cbEVT_EDITOR_BEFORE_SAVE );
  m_pControl->EndUndoAction();
  if( !cbSaveToFile( m_Filename, m_pControl->GetText(), GetEncoding(), GetUseBom() ) ) {
    wxString msg;
    msg.Printf( _T( "文件 %s 无法保存..." ), GetFilename().c_str() );
    msging( msg, _T( "保存文件时出错" ), wxICON_ERROR );
    return false;
  }
  wxFileName fname( m_Filename );
  m_LastModified = fname.GetModificationTime();
  m_IsOK = true;
  m_pControl->SetSavePoint();
  SetModified( false );
  NotifyPlugins( cbEVT_EDITOR_SAVE );
  return true;
}

bool M_Code_Editor::SaveAs() {
  wxFileName fname;
  fname.Assign( m_Filename );
  ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "app" ) );
  int StoredIndex = 0;
  wxString Filters = FileFilters::GetFilterString();
  wxString Path = fname.GetPath();
  wxString Extension = fname.GetExt();
  wxString Filter;
  if( !Extension.IsEmpty() ) {
    Extension.Prepend( _T( "." ) );
    Filter = FileFilters::GetFilterString( Extension );
  } else if( mgr ) {
    Filter = mgr->Read( _T( "/file_dialogs/save_file_as/filter" ), _T( "C/C++ files" ) );
  }
  if( !Filter.IsEmpty() ) {
    int sep = Filter.find( _T( "|" ) );
    if( sep != wxNOT_FOUND ) {
      Filter.Truncate( sep );
    }
    if( !Filter.IsEmpty() ) {
      FileFilters::GetFilterIndexFromName( Filters, Filter, StoredIndex );
    }
  }
  if( mgr && Path.IsEmpty() ) {
    Path = mgr->Read( _T( "/file_dialogs/save_file_as/directory" ), Path );
  }
  wxFileDialog dlg( Manager::Get()->GetAppWindow(), _T( "Save file" ), Path, fname.GetFullName(),
                    wxEmptyString, wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  dlg.SetWildcard( Filters );
  dlg.SetFilterIndex( StoredIndex );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() != wxID_OK ) {
    return false;
  }
  m_Filename = dlg.GetPath();
  Manager::Get()->GetLogManager()->Log( m_Filename );
  fname.Assign( m_Filename );
  m_Shortname = fname.GetFullName();
  SetEditorTitle( m_Shortname );
  SetProjectFile( nullptr );
  m_IsOK = true;
  SetLanguage( HL_AUTO );
  SetModified( true );
  SetEditorStyleAfterFileOpen();
  OverrideUseTabsPerLanguage( m_pControl );
  OverrideUseTabsPerLanguage( m_pControl2 );
  if( mgr ) {
    int Index = dlg.GetFilterIndex();
    Filter.Empty();
    if( FileFilters::GetFilterNameFromIndex( Filters, Index, Filter ) ) {
      mgr->Write( _T( "/file_dialogs/save_file_as/filter" ), Filter );
    }
    wxString Test = dlg.GetDirectory();
    mgr->Write( _T( "/file_dialogs/save_file_as/directory" ), dlg.GetDirectory() );
  }
  return Save();
}

bool M_Code_Editor::SaveFoldState() {
  bool bRet = false;
  if( ( m_foldBackup = CreateEditor() ) ) {
    ApplyStyles( m_foldBackup );
    m_foldBackup->SetText( m_pControl->GetText() );
    int count = m_pControl->GetLineCount();
    for( int i = 0; i < count; ++i ) {
      m_foldBackup->SetFoldLevel( i, m_pControl->GetFoldLevel( i ) );
    }
    bRet = true;
  }
  return bRet;
}

bool M_Code_Editor::FixFoldState() {
  bool bRet = false;
  if( m_foldBackup ) {
    int backupLength = m_foldBackup->GetLineCount();
    int realLength = m_pControl->GetLineCount();
    if( backupLength == realLength ) {
      ConfigManager* mgr = Manager::Get()->GetConfigManager( _T( "editor" ) );
      if( mgr->ReadBool( _T( "/folding/show_folds" ), true ) ) {
        int count = m_pControl->GetLineCount();
        for( int i = 0; i < count; ++i ) {
          int oldFoldLevel = m_foldBackup->GetFoldLevel( i );
          int newFoldLevel = m_pControl->GetFoldLevel( i );
          if( oldFoldLevel != newFoldLevel ) {
            if( m_pControl->GetLineVisible( i ) == true )
            { m_pControl->SetFoldExpanded( i, true ); }
            else {
              int parent = m_foldBackup->GetFoldParent( i );
              while( parent != -1 ) {
                m_pControl->ToggleFold( parent );
                parent = m_foldBackup->GetFoldParent( parent );
              }
              m_pControl->ShowLines( i, i );
              parent = m_foldBackup->GetFoldParent( i );
              while( parent != -1 ) {
                m_pControl->ToggleFold( parent );
                parent = m_foldBackup->GetFoldParent( parent );
              }
            }
          }
        }
      }
      bRet = true;
    }
    m_foldBackup->Destroy();
    m_foldBackup = nullptr;
  }
  return bRet;
}

void M_Code_Editor::AutoComplete() {
  Manager::Get()->GetLogManager()->Log( _T( "M_Code_Editor::AutoComplete() 过时了.\n使用缩写插件中的自动完成（M_Code_Editor &ed）." ) );
}

void M_Code_Editor::DoFoldAll( int fold ) {
  cbAssert( m_pControl );
  if( m_SplitType != stNoSplit ) {
    cbAssert( m_pControl2 );
  }
  M_Code_StyledTextCtrl* ctrl = GetControl();
  ctrl->Colourise( 0, -1 );
  int count = ctrl->GetLineCount();
  for( int i = 0; i <= count; ++i ) {
    DoFoldLine( i, fold );
  }
}

void M_Code_Editor::DoFoldBlockFromLine( int line, int fold ) {
  cbAssert( m_pControl );
  if( m_SplitType != stNoSplit ) {
    cbAssert( m_pControl2 );
  }
  M_Code_StyledTextCtrl* ctrl = GetControl();
  ctrl->Colourise( 0, -1 );
  int i, parent, maxLine, level, UnfoldUpto = line;
  parent = ctrl->GetFoldParent( line );
  level = ctrl->GetFoldLevel( parent );
  if( fold == 0 ) {
    do {
      if( !ctrl->GetFoldExpanded( parent ) ) {
        UnfoldUpto = parent;
      }
      if( wxSCI_FOLDLEVELBASE == ( level & wxSCI_FOLDLEVELNUMBERMASK ) ) {
        break;
      }
      parent = ctrl->GetFoldParent( parent );
      level = ctrl->GetFoldLevel( parent );
    } while( parent != -1 );
  }
  maxLine = ctrl->GetLastChild( line, -1 );
  for( i = UnfoldUpto; i <= maxLine; ++i ) {
    DoFoldLine( i, fold );
  }
}

bool M_Code_Editor::DoFoldLine( int line, int fold ) {
  cbAssert( m_pControl );
  if( m_SplitType != stNoSplit ) {
    cbAssert( m_pControl2 );
  }
  M_Code_StyledTextCtrl* ctrl = GetControl();
  int level = ctrl->GetFoldLevel( line );
  if( level & wxSCI_FOLDLEVELHEADERFLAG ) {
    bool IsExpanded = ctrl->GetFoldExpanded( line );
    if( fold == 0 &&  IsExpanded ) {
      return true;
    }
    if( fold == 1 && !IsExpanded ) {
      return true;
    }
    if( m_pData->mFoldingLimit && IsExpanded ) {
      if( ( level & wxSCI_FOLDLEVELNUMBERMASK ) > ( wxSCI_FOLDLEVELBASE + m_pData->mFoldingLimitLevel - 1 ) ) {
        return false;
      }
    }
    ctrl->ToggleFold( line );
    return true;
  }
  return false;
}

void M_Code_Editor::FoldAll() {
  DoFoldAll( 1 );
}

void M_Code_Editor::UnfoldAll() {
  DoFoldAll( 0 );
}

void M_Code_Editor::ToggleAllFolds() {
  DoFoldAll( 2 );
}

void M_Code_Editor::SetFoldingIndicator() {
  wxColor f( 0xff, 0xff, 0xff );
  wxColor b( 0x80, 0x80, 0x80 );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDEROPEN, wxSCI_MARK_CIRCLEMINUS, f, b );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDER, wxSCI_MARK_CIRCLEPLUS, f, b );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDERSUB, wxSCI_MARK_VLINE, f, b );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDERTAIL, wxSCI_MARK_LCORNERCURVE, f, b );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDEREND, wxSCI_MARK_CIRCLEPLUSCONNECTED, f, b );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDEROPENMID, wxSCI_MARK_CIRCLEMINUSCONNECTED, f, b );
  SetMarkerStyle( wxSCI_MARKNUM_FOLDERMIDTAIL, wxSCI_MARK_TCORNER, f, b );
}

void M_Code_Editor::FoldBlockFromLine( int line ) {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  DoFoldBlockFromLine( line, 1 );
}

void M_Code_Editor::UnfoldBlockFromLine( int line ) {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  DoFoldBlockFromLine( line, 0 );
}

void M_Code_Editor::ToggleFoldBlockFromLine( int line ) {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  DoFoldBlockFromLine( line, 2 );
}

void M_Code_Editor::GotoLine( int line, bool centerOnScreen ) {
  M_Code_StyledTextCtrl* control = GetControl();
  control->EnsureVisible( line );
  DoFoldLine( line, 0 );
  DoFoldLine( line + 1, 0 );
  if( centerOnScreen ) {
    int linesOnScreen = control->LinesOnScreen() >> 1;
    int firstVisibleLine = control->GetFirstVisibleLine();
    if( ( line <  firstVisibleLine ) || ( line > ( firstVisibleLine + 2 * linesOnScreen ) ) ) {
      control->GotoLine( line - linesOnScreen );
      control->GotoLine( line + linesOnScreen );
    }
  }
  control->GotoLine( line );
}

bool M_Code_Editor::GotoTokenPosition( int line, const wxString& tokenName ) {
  M_Code_StyledTextCtrl* control = GetControl();
  if( line > control->GetLineCount() ) {
    return false;
  }
  GotoLine( line, true );
  SetFocus();
  const int startPos = control->GetCurrentPos();
  const int endPos   = startPos + control->LineLength( line );
  if( endPos <= startPos ) {
    return false;
  }
  int tokenPos = control->FindText( startPos, endPos, tokenName, wxSCI_FIND_WHOLEWORD | wxSCI_FIND_MATCHCASE, nullptr );
  if( tokenPos != wxSCI_INVALID_POSITION ) {
    control->SetSelectionInt( tokenPos, tokenPos + tokenName.Len() );
  } else {
    control->GotoPos( startPos );
  }
  return true;
}

void M_Code_Editor::BreakpointMarkerToggle( int line ) {
  int marker = m_pControl->MarkerGet( line );
  if( marker & ( 1 << BREAKPOINT_MARKER ) ) {
    m_pControl->MarkerDelete( line, BREAKPOINT_MARKER );
  } else if( marker & ( 1 << BREAKPOINT_DISABLED_MARKER ) ) {
    m_pControl->MarkerDelete( line, BREAKPOINT_DISABLED_MARKER );
  } else {
    m_pControl->MarkerAdd( line, BREAKPOINT_MARKER );
  }
}

bool M_Code_Editor::AddBreakpoint( int line, bool notifyDebugger ) {
  if( HasBreakpoint( line ) ) {
    return false;
  }
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  if( !notifyDebugger ) {
    BreakpointMarkerToggle( line );
    return false;
  }
  DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
  if( dbgManager->GetBreakpointDialog()->AddBreakpoint( dbgManager->GetActiveDebugger(), m_Filename, line + 1 ) ) {
    BreakpointMarkerToggle( line );
    return true;
  }
  return false;
}

bool M_Code_Editor::RemoveBreakpoint( int line, bool notifyDebugger ) {
  if( !HasBreakpoint( line ) ) {
    return false;
  }
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  if( !notifyDebugger ) {
    BreakpointMarkerToggle( line );
    return false;
  }
  DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
  if( dbgManager->GetBreakpointDialog()->RemoveBreakpoint( dbgManager->GetActiveDebugger(), m_Filename, line + 1 ) ) {
    BreakpointMarkerToggle( line );
    return true;
  }
  return false;
}

void M_Code_Editor::ToggleBreakpoint( int line, bool notifyDebugger ) {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  if( !notifyDebugger ) {
    BreakpointMarkerToggle( line );
    return;
  }
  DebuggerManager *dbgManager = Manager::Get()->GetDebuggerManager();
  cbBreakpointsDlg *dialog = dbgManager->GetBreakpointDialog();
  cbDebuggerPlugin *plugin = dbgManager->GetActiveDebugger();
  if( !plugin || !plugin->SupportsFeature( cbDebuggerFeature::Breakpoints ) ) {
    return;
  }
  bool toggle = false;
  if( HasBreakpoint( line ) ) {
    if( dialog->RemoveBreakpoint( plugin, m_Filename, line + 1 ) ) {
      toggle = true;
    }
  } else {
    if( dialog->AddBreakpoint( plugin, m_Filename, line + 1 ) ) {
      toggle = true;
    }
  }
  if( toggle ) {
    BreakpointMarkerToggle( line );
    dialog->Reload();
  }
}

bool M_Code_Editor::HasBreakpoint( int line ) const {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  return LineHasMarker( BREAKPOINT_MARKER, line ) || LineHasMarker( BREAKPOINT_DISABLED_MARKER, line );
}

void M_Code_Editor::GotoNextBreakpoint() {
  MarkerNext( BREAKPOINT_MARKER );
}

void M_Code_Editor::GotoPreviousBreakpoint() {
  MarkerPrevious( BREAKPOINT_MARKER );
}

void M_Code_Editor::ToggleBookmark( int line ) {
  MarkerToggle( BOOKMARK_MARKER, line );
}

void M_Code_Editor::RefreshBreakpointMarkers() {
  M_Code_StyledTextCtrl *c = GetControl();
  int line = 0;
  while( ( line = c->MarkerNext( line, ( 1 << BREAKPOINT_MARKER ) ) ) != -1 ) {
    MarkerToggle( BREAKPOINT_MARKER, line );
  }
  line = 0;
  while( ( line = c->MarkerNext( line, ( 1 << BREAKPOINT_DISABLED_MARKER ) ) ) != -1 ) {
    MarkerToggle( BREAKPOINT_DISABLED_MARKER, line );
  }
  line = 0;
  while( ( line = c->MarkerNext( line, ( 1 << BREAKPOINT_OTHER_MARKER ) ) ) != -1 ) {
    MarkerToggle( BREAKPOINT_OTHER_MARKER, line );
  }
  const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
  for( DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it ) {
    const cbDebuggerPlugin *debugger = it->first;
    if( debugger == Manager::Get()->GetDebuggerManager()->GetActiveDebugger() ) {
      for( int ii = 0; ii < debugger->GetBreakpointsCount(); ++ii ) {
        cb::shared_ptr<const cbBreakpoint> bp = debugger->GetBreakpoint( ii );
        if( bp->GetLocation() == GetFilename() ) {
          if( bp->IsEnabled() ) {
            MarkerToggle( BREAKPOINT_MARKER, bp->GetLine() - 1 );
          } else
          { MarkerToggle( BREAKPOINT_DISABLED_MARKER, bp->GetLine() - 1 ); }
        }
      }
    } else {
      for( int ii = 0; ii < debugger->GetBreakpointsCount(); ++ii ) {
        cb::shared_ptr<const cbBreakpoint> bp = debugger->GetBreakpoint( ii );
        if( bp->GetLocation() == GetFilename() ) {
          MarkerToggle( BREAKPOINT_OTHER_MARKER, bp->GetLine() - 1 );
        }
      }
    }
  }
}

bool M_Code_Editor::HasBookmark( int line ) const {
  return LineHasMarker( BOOKMARK_MARKER, line );
}

void M_Code_Editor::GotoNextBookmark() {
  MarkerNext( BOOKMARK_MARKER );
}

void M_Code_Editor::GotoPreviousBookmark() {
  MarkerPrevious( BOOKMARK_MARKER );
}

void M_Code_Editor::ClearAllBookmarks() {
  M_Code_StyledTextCtrl* control = GetControl();
  control->MarkerDeleteAll( BOOKMARK_MARKER );
}

void M_Code_Editor::SetDebugLine( int line ) {
  MarkLine( DEBUG_MARKER, line );
  if( GetControl()->GetCaretLineVisible() ) {
    MarkLine( DEBUG_MARKER_HIGHLIGHT, line );
  }
  m_pData->m_LastDebugLine = line;
}

void M_Code_Editor::SetErrorLine( int line ) {
  MarkLine( ERROR_MARKER, line );
}

void M_Code_Editor::Undo() {
  cbAssert( GetControl() );
  GetControl()->Undo();
}

void M_Code_Editor::Redo() {
  cbAssert( GetControl() );
  GetControl()->Redo();
}

void M_Code_Editor::ClearHistory() {
  cbAssert( GetControl() );
  GetControl()->EmptyUndoBuffer( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/margin/use_changebar" ), true ) );
}

void M_Code_Editor::GotoNextChanged() {
  cbAssert( GetControl() );
  M_Code_StyledTextCtrl* p_Control = GetControl();
  int fromLine = p_Control->LineFromPosition( p_Control->GetCurrentPos() );
  int toLine = p_Control->GetLineCount() - 1;
  if( fromLine == toLine ) {
    fromLine = 0;
  } else {
    fromLine++;
  }
  int newLine = p_Control->FindChangedLine( fromLine, toLine );
  if( newLine != wxSCI_INVALID_POSITION ) {
    p_Control->GotoLine( newLine );
    p_Control->MakeNearbyLinesVisible( p_Control->GetCurrentLine() );
  }
}

void M_Code_Editor::GotoPreviousChanged() {
  cbAssert( GetControl() );
  M_Code_StyledTextCtrl* p_Control = GetControl();
  int fromLine = p_Control->LineFromPosition( p_Control->GetCurrentPos() );
  int toLine = 0;
  if( fromLine == toLine ) {
    fromLine = p_Control->GetLineCount() - 1;
  } else {
    fromLine--;
  }
  int newLine = p_Control->FindChangedLine( fromLine, toLine );
  if( newLine != wxSCI_INVALID_POSITION ) {
    p_Control->GotoLine( newLine );
    p_Control->MakeNearbyLinesVisible( p_Control->GetCurrentLine() );
  }
}

void M_Code_Editor::SetChangeCollection( bool collectChange ) {
  cbAssert( GetControl() );
  GetControl()->SetChangeCollection( collectChange );
}

void M_Code_Editor::Cut() {
  cbAssert( GetControl() );
  GetControl()->Cut();
}

void M_Code_Editor::Copy() {
  cbAssert( GetControl() );
  GetControl()->Copy();
}

void M_Code_Editor::Paste() {
  cbAssert( GetControl() );
  GetControl()->Paste();
}

bool M_Code_Editor::CanUndo() const {
  cbAssert( GetControl() );
  return !IsReadOnly() && GetControl()->CanUndo();
}

bool M_Code_Editor::CanRedo() const {
  cbAssert( GetControl() );
  return !IsReadOnly() && GetControl()->CanRedo();
}

bool M_Code_Editor::HasSelection() const {
  cbAssert( GetControl() );
  M_Code_StyledTextCtrl* control = GetControl();
  return control->GetSelectionStart() != control->GetSelectionEnd();
}

bool M_Code_Editor::CanPaste() const {
  cbAssert( GetControl() );
  return !IsReadOnly();
}

bool M_Code_Editor::IsReadOnly() const {
  cbAssert( GetControl() );
  return GetControl()->GetReadOnly();
}

void M_Code_Editor::SetReadOnly( bool readonly ) {
  cbAssert( GetControl() );
  GetControl()->SetReadOnly( readonly );
}

bool M_Code_Editor::LineHasMarker( int marker, int line ) const {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  return m_pControl->MarkerGet( line ) & ( 1 << marker );
}

void M_Code_Editor::MarkerToggle( int marker, int line ) {
  if( line == -1 ) {
    line = GetControl()->GetCurrentLine();
  }
  if( LineHasMarker( marker, line ) ) {
    GetControl()->MarkerDelete( line, marker );
  } else {
    GetControl()->MarkerAdd( line, marker );
  }
}

void M_Code_Editor::MarkerNext( int marker ) {
  int line = GetControl()->GetCurrentLine() + 1;
  int newLine = GetControl()->MarkerNext( line, 1 << marker );
  if( newLine == -1 ) {
    line = 0;
    newLine = GetControl()->MarkerNext( line, 1 << marker );
    if( newLine != -1 ) {
      InfoWindow::Tip( _T( "已到达文档结尾" ), _T( "查找书签操作" ), 1000 );
    }
  }
  if( newLine != -1 ) {
    GotoLine( newLine );
  }
}

void M_Code_Editor::MarkerPrevious( int marker ) {
  int line = GetControl()->GetCurrentLine() - 1;
  int newLine = GetControl()->MarkerPrevious( line, 1 << marker );
  if( newLine == -1 ) {
    line = GetControl()->GetLineCount();
    newLine = GetControl()->MarkerPrevious( line, 1 << marker );
    if( newLine != -1 ) {
      InfoWindow::Tip( _T( "已到达文档结尾" ), _T( "查找书签操作" ), 1000 );
    }
  }
  if( newLine != -1 ) {
    GotoLine( newLine );
  }
}

void M_Code_Editor::MarkLine( int marker, int line ) {
  if( line == -1 ) {
    GetControl()->MarkerDeleteAll( marker );
  } else {
    GetControl()->MarkerAdd( line, marker );
  }
}

void M_Code_Editor::GotoMatchingBrace() {
  M_Code_StyledTextCtrl* control = GetControl();
  int matchingBrace = control->BraceMatch( control->GetCurrentPos() );
  if( matchingBrace == wxSCI_INVALID_POSITION ) {
    matchingBrace = control->BraceMatch( control->GetCurrentPos() - 1 );
  } else {
    ++matchingBrace;
  }
  if( matchingBrace == wxSCI_INVALID_POSITION ) {
    wxRegEx ppIf( _T( "^[ \t]*#[ \t]*if" ) );
    wxRegEx ppElse( _T( "^[ \t]*#[ \t]*el" ) );
    wxRegEx ppEnd( _T( "^[ \t]*#[ \t]*endif" ) );
    wxRegEx pp( _T( "^[ \t]*#[ \t]*[a-z]*" ) );
    if( ppIf.Matches( control->GetCurLine() ) || ppElse.Matches( control->GetCurLine() ) ) {
      int depth = 1;
      for( int i = control->GetCurrentLine() + 1; i < control->GetLineCount(); ++i ) {
        if( control->GetLine( i ).Find( _T( '#' ) ) != wxNOT_FOUND ) {
          if( ppIf.Matches( control->GetLine( i ) ) ) {
            ++depth;
          } else if( ppEnd.Matches( control->GetLine( i ) ) ) {
            --depth;
          }
        }
        if( depth == 0 ) {
          pp.Matches( control->GetLine( i ) );
          matchingBrace = control->PositionFromLine( i ) + pp.GetMatch( control->GetLine( i ) ).Length();
          break;
        }
      }
    } else if( ppEnd.Matches( control->GetCurLine() ) ) {
      int depth = -1;
      for( int i = control->GetCurrentLine() - 1; i >= 0; --i ) {
        if( control->GetLine( i ).Find( _T( '#' ) ) != wxNOT_FOUND ) {
          if( ppIf.Matches( control->GetLine( i ) ) ) {
            ++depth;
          } else if( ppEnd.Matches( control->GetLine( i ) ) ) {
            --depth;
          }
        }
        if( depth == 0 ) {
          pp.Matches( control->GetLine( i ) );
          matchingBrace = control->PositionFromLine( i ) + pp.GetMatch( control->GetLine( i ) ).Length();
          break;
        }
      }
    }
  }
  if( matchingBrace != wxSCI_INVALID_POSITION ) {
    control->GotoPos( matchingBrace );
    control->ChooseCaretX();
    control->MakeNearbyLinesVisible( control->GetCurrentLine() );
  }
}

void M_Code_Editor::HighlightBraces() {
  M_Code_StyledTextCtrl* control = GetControl();
  int currPos = control->GetCurrentPos();
  int newPos = control->BraceMatch( currPos );
  if( newPos == wxSCI_INVALID_POSITION ) {
    if( currPos > 0 ) {
      currPos--;
    }
    newPos = control->BraceMatch( currPos );
  }
  wxChar ch = control->GetCharAt( currPos );
  if( ch == _T( '{' ) || ch == _T( '[' ) || ch == _T( '(' ) || ch == _T( '}' ) || ch == _T( ']' ) || ch == _T( ')' ) ) {
    if( newPos != wxSCI_INVALID_POSITION ) {
      control->BraceHighlight( currPos, newPos );
      const int currColum = control->GetColumn( currPos );
      const int newColum = control->GetColumn( newPos );
      control->SetHighlightGuide( ( currColum < newColum ) ? currColum : newColum );
    } else
    { control->BraceBadLight( currPos ); }
  } else {
    control->BraceHighlight( -1, -1 );
  }
}

int M_Code_Editor::GetLineIndentInSpaces( int line ) const {
  M_Code_StyledTextCtrl* control = GetControl();
  int currLine = ( line == -1 ) ? control->LineFromPosition( control->GetCurrentPos() ) : line;
  wxString text = control->GetLine( currLine );
  unsigned int len = text.Length();
  int spaceCount = 0;
  for( unsigned int i = 0; i < len; ++i ) {
    if( text[i] == _T( ' ' ) ) {
      ++spaceCount;
    } else if( text[i] == _T( '\t' ) ) {
      spaceCount += control->GetTabWidth();
    } else {
      break;
    }
  }
  return spaceCount;
}

wxString M_Code_Editor::GetLineIndentString( int line ) const {
  return M_Code_EditorInternalData::GetLineIndentString( line, GetControl() );
}

void M_Code_Editor::AddToContextMenu( wxMenu* popup, ModuleType type ) { // 编辑器右键主菜单
  bool noeditor = ( type != mtEditorManager );
  wxMenu *SubMenu = nullptr;
  M_Code_StyledTextCtrl* control = GetControl();
  wxString Str = control->GetSelectedText();
  bool hasSel = control->GetSelectionEnd() - control->GetSelectionStart() != 0;
  if( !noeditor ) {
    if( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/folding/show_folds" ), false ) ) {
      SubMenu = new wxMenu;
      SubMenu->Append( idFoldingFoldAll, _T( "全部折叠" ) );
      SubMenu->Append( idFoldingUnfoldAll, _T( "全部展开" ) );
      SubMenu->Append( idFoldingToggleAll, _T( "切换折叠" ) );
      SubMenu->Append( idFoldingFoldCurrent, _T( "折叠" ) );
      SubMenu->Append( idFoldingUnfoldCurrent, _T( "展开" ) );
      SubMenu->Append( idFoldingToggleCurrent, _T( "切换" ) );
      popup->Append( idFolding, _T( "折叠" ), SubMenu );
    }
  }
  if( !Str.IsEmpty() ) {
    if( !control->GetReadOnly() && hasSel ) {
      popup->Append( idCut, _T( "Cut" ) );
    }
    if( hasSel ) {
      popup->Append( idCopy, _T( "复制" ) );
    }
  }
  if( !control->GetReadOnly() && control->CanPaste() ) {
    popup->Append( idPaste, _T( "粘贴" ) );
  }
  if( !control->GetReadOnly() && hasSel ) {
    popup->Append( idDelete, _T( "删除" ) );
  }
  if( Str.IsAscii() && !Str.IsEmpty() ) {
    if( !control->GetReadOnly() && hasSel ) {
      popup->Append( idUpperCase, _T( "转换为大写" ) );
      popup->Append( idLowerCase, _T( "转换为小写" ) );
    }
  }
  if( control->CanUndo() ) {
    popup->Append( idUndo, _T( "撤销" ) );
  }
  if( control->CanRedo() ) {
    popup->Append( idRedo, _T( "恢复" ) );
  }
  if( control->CanUndo() || control->CanRedo() ) {
    popup->Append( idClearHistory, _T( "清除修改历史" ) );
  }
  popup->Append( idSelectAll, _T( "全选" ) );
  SubMenu = new wxMenu;
  SubMenu->Append( idBookmarksToggle, _T( "切换书签" ) );
  SubMenu->Append( idBookmarksPrevious, _T( "上一个书签" ) );
  SubMenu->Append( idBookmarksNext, _T( "下一个书签" ) );
  SubMenu->Append( idBookmarksClearAll, _T( "清除所有书签" ) );
  popup->Append( idBookmarks, _T( "书签" ), SubMenu );
  SubMenu = new wxMenu;
  SubMenu->Append( idEmptyMenu, _T( "Empty" ) );
  popup->Append( idInsert, _T( "插入" ), SubMenu );
  if( !noeditor && !m_pData->GetUrl().IsEmpty() ) {
    popup->Insert( 0, idOpenUrl, _T( "在浏览器中打开链接" ) );
  }
  if( Manager::Get()->GetProjectManager()->GetActiveProject() ) {
    bool isAddRemoveEnabled = true;
    isAddRemoveEnabled = Manager::Get()->GetProjectManager()->GetActiveProject()->GetCurrentlyCompilingTarget() == nullptr;
    if( !m_pProjectFile && isAddRemoveEnabled ) {
      popup->Append( idAddFileToProject, _T( "将文件添加到活动项目" ) );
    }
  }
  SubMenu = nullptr;
  wxMenuItem* insertitem = popup->FindItem( idInsert );
  if( insertitem ) {
    SubMenu = insertitem->GetSubMenu();
  }
  if( SubMenu ) {
    if( SubMenu->GetMenuItemCount() > 1 ) {
      SubMenu->Delete( idEmptyMenu );
    }
  }
}

bool M_Code_Editor::OnBeforeBuildContextMenu( const wxPoint& position, ModuleType type ) {
  bool noeditor = ( type != mtEditorManager );
  if( !noeditor && position != wxDefaultPosition ) {
    wxPoint clientpos( ScreenToClient( position ) );
    const int margin = m_pControl->GetMarginWidth( C_LINE_MARGIN ) +
                       m_pControl->GetMarginWidth( C_MARKER_MARGIN ) +
                       m_pControl->GetMarginWidth( C_FOLDING_MARGIN ) +
                       m_pControl->GetMarginWidth( C_CHANGEBAR_MARGIN );
    wxRect r = m_pControl->GetRect();
    bool inside1 = r.Contains( clientpos );
    M_Code_StyledTextCtrl* control = !m_pControl2 || inside1 ? m_pControl : m_pControl2;
    clientpos = control->ScreenToClient( position );
    if( clientpos.x < margin ) {
      int pos = control->PositionFromPoint( clientpos );
      m_pData->m_LastMarginMenuLine = control->LineFromPosition( pos );
      wxMenu* popup = new wxMenu;
      cbDebuggerPlugin *plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
      if( plugin && plugin->SupportsFeature( cbDebuggerFeature::Breakpoints ) ) {
        bool hasBreak = LineHasMarker( BREAKPOINT_MARKER, m_pData->m_LastMarginMenuLine );
        bool hasBreakDisabled = LineHasMarker( BREAKPOINT_DISABLED_MARKER, m_pData->m_LastMarginMenuLine );
        if( hasBreak || hasBreakDisabled ) {
          popup->Append( idBreakpointEdit, _T( "编辑断点" ) );
          popup->Append( idBreakpointRemove, _T( "删除断点" ) );
          if( hasBreak ) {
            popup->Append( idBreakpointDisable, _T( "禁用断点" ) );
          }
          if( hasBreakDisabled ) {
            popup->Append( idBreakpointEnable, _T( "启用断点" ) );
          }
        } else {
          popup->Append( idBreakpointAdd, _T( "添加断点" ) );
        }
      }
      if( LineHasMarker( BOOKMARK_MARKER, m_pData->m_LastMarginMenuLine ) ) {
        popup->Append( idBookmarkRemove, _T( "删除书签" ) );
      } else {
        popup->Append( idBookmarkAdd, _T( "加入书签" ) );
      }
      popup->Append( idBookmarkRemoveAll, _T( "清空书签" ) );
      PopupMenu( popup );
      delete popup;
      return false;
    }
    const int pos = control->PositionFromPoint( control->ScreenToClient( wxGetMousePosition() ) );
    if( control->GetSelectionStart() > pos || control->GetSelectionEnd() < pos ) {
      control->GotoPos( pos );
    }
  }
  return EditorBase::OnBeforeBuildContextMenu( position, type );
}

void M_Code_Editor::OnContextMenuEntry( wxCommandEvent& event ) {
  M_Code_StyledTextCtrl* control = GetControl();
  const int id = event.GetId();
  if( id == idUndo ) {
    control->Undo();
  } else if( id == idRedo ) {
    control->Redo();
  } else if( id == idClearHistory ) {
    control->EmptyUndoBuffer( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/margin/use_changebar" ), true ) );
  } else if( id == idCut ) {
    control->Cut();
  } else if( id == idCopy ) {
    control->Copy();
  } else if( id == idPaste ) {
    control->Paste();
  } else if( id == idDelete ) {
    control->ReplaceSelection( wxEmptyString );
  } else if( id == idUpperCase ) {
    control->UpperCase();
  } else if( id == idLowerCase ) {
    control->LowerCase();
  } else if( id == idSelectAll ) {
    control->SelectAll();
  } else if( id == idSwapHeaderSource ) {
    Manager::Get()->GetEditorManager()->SwapActiveHeaderSource();
  } else if( id == idOpenContainingFolder ) {
    Manager::Get()->GetEditorManager()->OpenContainingFolder();
  } else if( id == idBookmarkAdd ) {
    control->MarkerAdd( m_pData->m_LastMarginMenuLine, BOOKMARK_MARKER );
  } else if( id == idBookmarkRemove ) {
    control->MarkerDelete( m_pData->m_LastMarginMenuLine, BOOKMARK_MARKER );
  } else if( id == idBookmarkRemoveAll ) {
    control->MarkerDeleteAll( BOOKMARK_MARKER );
  } else if( id == idBookmarksToggle ) {
    MarkerToggle( BOOKMARK_MARKER );
  } else if( id == idBookmarksNext ) {
    MarkerNext( BOOKMARK_MARKER );
  } else if( id == idBookmarksPrevious ) {
    MarkerPrevious( BOOKMARK_MARKER );
  } else if( id == idBookmarksClearAll ) {
    control->MarkerDeleteAll( BOOKMARK_MARKER );
  } else if( id == idFoldingFoldAll ) {
    FoldAll();
  } else if( id == idFoldingUnfoldAll ) {
    UnfoldAll();
  } else if( id == idFoldingToggleAll ) {
    ToggleAllFolds();
  } else if( id == idFoldingFoldCurrent ) {
    FoldBlockFromLine();
  } else if( id == idFoldingUnfoldCurrent ) {
    UnfoldBlockFromLine();
  } else if( id == idFoldingToggleCurrent ) {
    ToggleFoldBlockFromLine();
  } else if( id == idOpenUrl ) {
    wxLaunchDefaultBrowser( m_pData->GetUrl() );
  } else if( id == idSplitHorz ) {
    Split( stHorizontal );
  } else if( id == idSplitVert ) {
    Split( stVertical );
  } else if( id == idUnsplit ) {
    Unsplit();
  } else if( id == idProperties ) {
    if( m_pProjectFile ) {
      m_pProjectFile->ShowOptions( this );
    } else {
      ProjectFileOptionsDlg dlg( this, GetFilename() ,nullptr);
      PlaceWindow( &dlg );
      dlg.ShowModal();
    }
  } else if( id == idAddFileToProject ) {
    M_Code_Project *prj = Manager::Get()->GetProjectManager()->GetActiveProject();
    wxArrayInt targets;
    if( Manager::Get()->GetProjectManager()->AddFileToProject( m_Filename, prj, targets ) != 0 ) {
      ProjectFile* pf = prj->GetFileByFilename( m_Filename, false );
      SetProjectFile( pf );
      Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
    }
  } else if( id == idRemoveFileFromProject ) {
    if( m_pProjectFile ) {
      M_Code_Project *prj = m_pProjectFile->GetParentProject();
      Manager::Get()->GetProjectManager()->RemoveFileFromProject( m_pProjectFile, prj );
      Manager::Get()->GetProjectManager()->GetUI().RebuildTree();
    }
  } else if( id == idShowFileInProject ) {
    cbProjectManagerUI& ui = Manager::Get()->GetProjectManager()->GetUI();
    ui.SwitchToProjectsPage();
    ui.ShowFileInTree( *m_pProjectFile );
  } else if( id == idBreakpointAdd ) {
    AddBreakpoint( m_pData->m_LastMarginMenuLine );
  } else if( id == idBreakpointEdit ) {
    cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
    dialog->EditBreakpoint( m_Filename, m_pData->m_LastMarginMenuLine + 1 );
  } else if( id == idBreakpointRemove ) {
    RemoveBreakpoint( m_pData->m_LastMarginMenuLine );
  } else if( id == idBreakpointEnable ) {
    cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
    dialog->EnableBreakpoint( m_Filename, m_pData->m_LastMarginMenuLine + 1, true );
  } else if( id == idBreakpointDisable ) {
    cbBreakpointsDlg *dialog = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
    dialog->EnableBreakpoint( m_Filename, m_pData->m_LastMarginMenuLine + 1, false );
  } else {
    event.Skip();
  }
}

void M_Code_Editor::OnMarginClick( wxScintillaEvent& event ) {
  switch( event.GetMargin() ) {
    case C_MARKER_MARGIN: {
      int lineYpix = event.GetPosition();
      int line = GetControl()->LineFromPosition( lineYpix );
      ToggleBreakpoint( line );
      break;
    }
    case C_FOLDING_MARGIN: {
      int lineYpix = event.GetPosition();
      int line = GetControl()->LineFromPosition( lineYpix );
      GetControl()->ToggleFold( line );
      break;
    }
    default:
      break;
  }
  OnScintillaEvent( event );
}

void M_Code_Editor::OnEditorUpdateUI( wxScintillaEvent& event ) {
  if( Manager::Get()->GetEditorManager()->GetActiveEditor() == this ) {
    NotifyPlugins( cbEVT_EDITOR_UPDATE_UI );
    HighlightBraces();
  }
  OnScintillaEvent( event );
}

void M_Code_Editor::OnEditorChange( wxScintillaEvent& event ) {
  SetModified( m_pControl->GetModify() );
  OnScintillaEvent( event );
}

void M_Code_Editor::OnEditorCharAdded( wxScintillaEvent& event ) {
  m_autoIndentDone = false;
  OnScintillaEvent( event );
  if( !m_autoIndentDone ) {
    const wxChar ch = event.GetKey();
    M_Code_StyledTextCtrl* control = GetControl();
    if( ( ch == _T( '\n' ) ) || ( ( control->GetEOLMode() == wxSCI_EOL_CR ) && ( ch == _T( '\r' ) ) ) ) {
      const int pos = control->GetCurrentPos();
      const int currLine = control->LineFromPosition( pos );
      const bool autoIndent = Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/auto_indent" ), true );
      if( autoIndent && currLine > 0 ) {
        wxString indent;
        if( control->GetCurLine().Trim().IsEmpty() ) {
          for( int i = currLine - 1; i >= 0; --i ) {
            const wxString& prevLineStr = control->GetLine( i );
            if( !( prevLineStr.IsEmpty() || prevLineStr[0] == _T( '\n' ) || prevLineStr[0] == _T( '\r' ) ) ) {
              indent = GetLineIndentString( i );
              break;
            }
          }
        } else
        { indent = GetLineIndentString( currLine - 1 ); }
        if( !indent.IsEmpty() ) {
          control->BeginUndoAction();
          control->InsertText( pos, indent );
          control->GotoPos( pos + indent.Length() );
          control->ChooseCaretX();
          control->EndUndoAction();
        }
      }
    }
    bool braceCompleted = false;
    if( Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/selection_brace_completion" ), false )
        || control->IsBraceShortcutActive() ) {
      braceCompleted = control->DoSelectionBraceCompletion( ch );
    }
    if( !braceCompleted && Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/brace_completion" ), true ) ) {
      control->DoBraceCompletion( ch );
    }
  }
}

void M_Code_Editor::AutoIndentDone() {
  m_autoIndentDone = true;
}

void M_Code_Editor::OnEditorDwellStart( wxScintillaEvent& event ) {
  if( !wxTheApp->IsActive() ) {
    return;
  }
  M_Code_StyledTextCtrl* control = GetControl();
  if( !control ) {
    return;
  }
  wxRect screenRect = control->GetScreenRect();
  wxPoint ptEvent( event.GetX(), event.GetY() );
  ptEvent = control->ClientToScreen( ptEvent );
  wxPoint ptScreen = wxGetMousePosition();
  wxPoint ptClient = control->ScreenToClient( ptScreen );
  double distance = sqrt( ( ptScreen.x - ptEvent.x ) * ( ptScreen.x - ptEvent.x ) + ( ptScreen.y - ptEvent.y ) * ( ptScreen.y - ptEvent.y ) );
  if( !screenRect.Contains( ptScreen ) || distance > 10 ) {
    return;
  }
  int pos = control->PositionFromPoint( ptClient );
  int style = control->GetStyleAt( pos );
  NotifyPlugins( cbEVT_EDITOR_TOOLTIP, style, wxEmptyString, ptClient.x, ptClient.y );
  wxScintillaEvent newEvent( event );
  newEvent.SetX( ptClient.x );
  newEvent.SetY( ptClient.y );
  OnScintillaEvent( event );
}

void M_Code_Editor::OnEditorDwellEnd( wxScintillaEvent& event ) {
  NotifyPlugins( cbEVT_EDITOR_TOOLTIP_CANCEL );
  OnScintillaEvent( event );
}

void M_Code_Editor::OnEditorModified( wxScintillaEvent& event ) {
  int linesAdded = event.GetLinesAdded();
  bool isAdd = event.GetModificationType() & wxSCI_MOD_INSERTTEXT;
  bool isDel = event.GetModificationType() & wxSCI_MOD_DELETETEXT;
  if( ( isAdd || isDel ) && linesAdded != 0 ) {
    m_pData->SetLineNumberColWidth();
    int startline = m_pControl->LineFromPosition( event.GetPosition() );
    if( m_pControl == event.GetEventObject() ) {
      const DebuggerManager::RegisteredPlugins &plugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
      cbDebuggerPlugin *active = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
      for( DebuggerManager::RegisteredPlugins::const_iterator it = plugins.begin(); it != plugins.end(); ++it ) {
        if( it->first != active ) {
          it->first->EditorLinesAddedOrRemoved( this, startline + 1, linesAdded );
        }
      }
      if( active ) {
        active->EditorLinesAddedOrRemoved( this, startline + 1, linesAdded );
      }
      cbBreakpointsDlg *dlg = Manager::Get()->GetDebuggerManager()->GetBreakpointDialog();
      if( dlg ) {
        dlg->Reload();
      }
      RefreshBreakpointMarkers();
    }
  }
  if( ( event.GetModificationType() & wxSCI_MOD_CHANGEFOLD )
      && ( event.GetFoldLevelPrev() & wxSCI_FOLDLEVELHEADERFLAG ) ) {
    M_Code_StyledTextCtrl* control = GetControl();
    int line = event.GetLine();
    if( ! control->GetFoldExpanded( line ) ) {
      control->SetFoldExpanded( line, true );
      control->ShowLines( line, control->GetLastChild( line, -1 ) );
    }
  }
  OnScintillaEvent( event );
}

void M_Code_Editor::OnUserListSelection( wxScintillaEvent& event ) {
  OnScintillaEvent( event );
}

void M_Code_Editor::OnClose( M_Code_unused wxCloseEvent& event ) {
  Manager::Get()->GetEditorManager()->Close( this );
}

void M_Code_Editor::DoIndent() {
  M_Code_StyledTextCtrl* control = GetControl();
  if( control ) {
    control->SendMsg( wxSCI_CMD_TAB );
  }
}

void M_Code_Editor::DoUnIndent() {
  M_Code_StyledTextCtrl* control = GetControl();
  if( control ) {
    control->SendMsg( wxSCI_CMD_BACKTAB );
  }
}

void M_Code_Editor::OnZoom( wxScintillaEvent& event ) {
  ConfigManager* mgr =  Manager::Get()->GetConfigManager( _T( "editor" ) );
  int zoom = GetControl()->GetZoom();
  Manager::Get()->GetEditorManager()->SetZoom( zoom );
  bool both = Manager::Get()->GetConfigManager( _T( "editor" ) )->ReadBool( _T( "/zoom_all" ) );
  if( both ) {
    Manager::Get()->GetEditorManager()->GetNotebook()->SetZoom( zoom );
  }
  m_pData->SetLineNumberColWidth( both );
  if( mgr->ReadBool( _T( "/folding/show_folds" ), true ) ) {
    m_pData->SetFoldingColWidth( both );
  }
  OnScintillaEvent( event );
}


void M_Code_Editor::SetZoom( int zoom, bool both ) {
  if( both ) {
    if( m_pControl->GetZoom() != zoom ) {
      m_pControl->SetZoom( zoom );
    }
    if( m_pControl2 && ( m_pControl2->GetZoom() != zoom ) ) {
      m_pControl2->SetZoom( zoom );
    }
  } else {
    if( GetControl()->GetZoom() != zoom ) {
      GetControl()->SetZoom( zoom );
    }
  }
}

void M_Code_Editor::OnScintillaEvent( wxScintillaEvent& event ) {
  if( !ProjectManager::IsBusy() && EditorHooks::HasRegisteredHooks() ) {
    EditorHooks::CallHooks( this, event );
  }
}

bool M_Code_Editor::CanSelectAll() const {
  int res = 0;
  M_Code_StyledTextCtrl* control = GetControl();
  if( control ) {
    res = control->GetLength();
  }
  return res > 0;
}

void M_Code_Editor::SelectAll() {
  M_Code_StyledTextCtrl* control = GetControl();
  if( control ) {
    control->SelectAll();
  }
}
